Skip to content

Commit

Permalink
Prerender: Move PrerenderHost::PageHolder into its own file
Browse files Browse the repository at this point in the history
PrerenderHost::PageHolder is big enough to have its own file. This CL
renames the class to PrerenderPageHolder and moves it into
prerender_page_hoder.{cc,h}.

This change is just refactoring to improve code readability and doesn't
change functional behavior.

Bug: 1350676
Change-Id: I3aa2b4ee3ccb8aebb8dbf13bcda55478dda950be
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3811087
Reviewed-by: Lingqi Chi <lingqi@chromium.org>
Commit-Queue: Hiroki Nakagawa <nhiroki@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1032430}
  • Loading branch information
nhiroki authored and Chromium LUCI CQ committed Aug 8, 2022
1 parent 308d247 commit 083d224
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 272 deletions.
2 changes: 2 additions & 0 deletions content/browser/BUILD.gn
Expand Up @@ -1456,6 +1456,8 @@ source_set("browser") {
"preloading/prerender/prerender_navigation_throttle.h",
"preloading/prerender/prerender_navigation_utils.cc",
"preloading/prerender/prerender_navigation_utils.h",
"preloading/prerender/prerender_page_holder.cc",
"preloading/prerender/prerender_page_holder.h",
"preloading/prerender/prerender_subframe_navigation_throttle.cc",
"preloading/prerender/prerender_subframe_navigation_throttle.h",
"preloading/speculation_rules/speculation_host_impl.cc",
Expand Down
271 changes: 2 additions & 269 deletions content/browser/preloading/prerender/prerender_host.cc
Expand Up @@ -13,17 +13,13 @@
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/preloading/prerender/prerender_host_registry.h"
#include "content/browser/preloading/prerender/prerender_metrics.h"
#include "content/browser/preloading/prerender/prerender_page_holder.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_controller_impl.h"
#include "content/browser/renderer_host/navigation_entry_restore_context_impl.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/page_impl.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_frame_proxy_host.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/site_info.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/navigation_controller.h"
Expand Down Expand Up @@ -73,269 +69,6 @@ PreloadingFailureReason ToPreloadingFailureReason(

} // namespace

class PrerenderHost::PageHolder : public FrameTree::Delegate,
public NavigationControllerDelegate {
public:
explicit PageHolder(WebContentsImpl& web_contents)
: web_contents_(web_contents),
frame_tree_(
std::make_unique<FrameTree>(web_contents.GetBrowserContext(),
this,
this,
&web_contents,
&web_contents,
&web_contents,
&web_contents,
&web_contents,
&web_contents,
FrameTree::Type::kPrerender,
base::UnguessableToken::Create())) {
scoped_refptr<SiteInstance> site_instance =
SiteInstance::Create(web_contents.GetBrowserContext());
frame_tree_->Init(site_instance.get(),
/*renderer_initiated_creation=*/false,
/*main_frame_name=*/"", /*opener=*/nullptr,
/*frame_policy=*/blink::FramePolicy());

// Use the same SessionStorageNamespace as the primary page for the
// prerendering page.
frame_tree_->controller().SetSessionStorageNamespace(
site_instance->GetStoragePartitionConfig(),
web_contents_.GetPrimaryFrameTree()
.controller()
.GetSessionStorageNamespace(
site_instance->GetStoragePartitionConfig()));

// TODO(https://crbug.com/1199679): This should be moved to FrameTree::Init
web_contents_.NotifySwappedFromRenderManager(
/*old_frame=*/nullptr,
frame_tree_->root()->render_manager()->current_frame_host());
}

~PageHolder() override {
// If we are still waiting on test loop, we can assume the page loading step
// has been cancelled and the PageHolder is being discarded without
// completing loading the page.
if (on_wait_loading_finished_)
std::move(on_wait_loading_finished_)
.Run(PrerenderHost::LoadingOutcome::kPrerenderingCancelled);

if (frame_tree_)
frame_tree_->Shutdown();
}

// FrameTree::Delegate

// TODO(https://crbug.com/1199682): Correctly handle load events. Ignored for
// now as it confuses WebContentsObserver instances because they can not
// distinguish between the different FrameTrees.

void DidStartLoading(FrameTreeNode* frame_tree_node,
bool should_show_loading_ui) override {}

void DidStopLoading() override {
if (on_wait_loading_finished_) {
std::move(on_wait_loading_finished_)
.Run(PrerenderHost::LoadingOutcome::kLoadingCompleted);
}
}

void DidChangeLoadProgress() override {}
bool IsHidden() override { return true; }
FrameTree* LoadingTree() override {
// For prerendering loading tree is the same as its frame tree as loading is
// done at a frame tree level in the background, unlike the loading visible
// to the user where we account for nested frame tree loading state.
return frame_tree_.get();
}
void NotifyPageChanged(PageImpl& page) override {}
int GetOuterDelegateFrameTreeNodeId() override {
// A prerendered FrameTree is not "inner to" or "nested inside" another
// FrameTree; it exists in parallel to the primary FrameTree of the current
// WebContents. Therefore, it must not attempt to access the primary
// FrameTree in the sense of an "outer delegate" relationship, so we return
// the invalid ID here.
return FrameTreeNode::kFrameTreeNodeInvalidId;
}
bool IsPortal() override { return false; }

// NavigationControllerDelegate
void NotifyNavigationStateChanged(InvalidateTypes changed_flags) override {}
void NotifyBeforeFormRepostWarningShow() override {}
void NotifyNavigationEntryCommitted(
const LoadCommittedDetails& load_details) override {}
void NotifyNavigationEntryChanged(
const EntryChangedDetails& change_details) override {}
void NotifyNavigationListPruned(
const PrunedDetails& pruned_details) override {}
void NotifyNavigationEntriesDeleted() override {}
void ActivateAndShowRepostFormWarningDialog() override {
// Not supported, cancel pending reload.
GetNavigationController().CancelPendingReload();
}
bool ShouldPreserveAbortedURLs() override { return false; }
WebContents* DeprecatedGetWebContents() override { return GetWebContents(); }
void UpdateOverridingUserAgent() override {}

NavigationControllerImpl& GetNavigationController() {
return frame_tree_->controller();
}

WebContents* GetWebContents() { return &web_contents_; }

FrameTree& GetPrimaryFrameTree() {
return web_contents_.GetPrimaryFrameTree();
}

std::unique_ptr<StoredPage> Activate(NavigationRequest& navigation_request) {
// There should be no ongoing main-frame navigation during activation.
// TODO(https://crbug.com/1190644): Make sure sub-frame navigations are
// fine.
DCHECK(!frame_tree_->root()->HasNavigation());

// Before the root's current_frame_host is cleared, collect the subframes of
// `frame_tree_` whose FrameTree will need to be updated.
FrameTree::NodeRange node_range = frame_tree_->Nodes();
std::vector<FrameTreeNode*> subframe_nodes(std::next(node_range.begin()),
node_range.end());

// Before the root's current_frame_host is cleared, collect the replication
// state so that it can be used for post-activation validation.
blink::mojom::FrameReplicationState prior_replication_state =
frame_tree_->root()->current_replication_state();

// Update FrameReplicationState::has_received_user_gesture_before_nav of the
// prerendered page.
//
// On regular navigation, it is updated via a renderer => browser IPC
// (RenderFrameHostImpl::HadStickyUserActivationBeforeNavigationChanged),
// which is sent from blink::DocumentLoader::CommitNavigation. However,
// this doesn't happen on prerender page activation, so the value is not
// correctly updated without this treatment.
//
// The updated value will be sent to the renderer on
// blink::mojom::Page::ActivatePrerenderedPage.
prior_replication_state.has_received_user_gesture_before_nav =
navigation_request.frame_tree_node()
->has_received_user_gesture_before_nav();

// frame_tree_->root(). Do not add any code between here and
// frame_tree_.reset() that calls into observer functions to minimize the
// duration of current_frame_host being null.
//
// TODO(https://crbug.com/1176148): Investigate how to combine taking the
// prerendered page and frame_tree_ destruction.
std::unique_ptr<StoredPage> page =
frame_tree_->root()->render_manager()->TakePrerenderedPage();

std::unique_ptr<NavigationEntryRestoreContextImpl> context =
std::make_unique<NavigationEntryRestoreContextImpl>();
std::unique_ptr<NavigationEntryImpl> nav_entry =
GetNavigationController()
.GetEntryWithUniqueID(page->render_frame_host->nav_entry_id())
->CloneWithoutSharing(context.get());

navigation_request.SetPrerenderActivationNavigationState(
std::move(nav_entry), prior_replication_state);

FrameTree& target_frame_tree = GetPrimaryFrameTree();
DCHECK_EQ(&target_frame_tree,
navigation_request.frame_tree_node()->frame_tree());

// We support activating the prerenderd page only to the topmost
// RenderFrameHost.
CHECK(!page->render_frame_host->GetParentOrOuterDocumentOrEmbedder());

page->render_frame_host->SetFrameTreeNode(*(target_frame_tree.root()));
// Copy frame name into the replication state of the primary main frame to
// ensure that the replication state of the primary main frame after
// activation matches the replication state stored in the renderer.
// TODO(https://crbug.com/1237091): Copying frame name here is suboptimal
// and ideally we'd do this at the same time when transferring the proxies
// from the StoredPage into RenderFrameHostManager. However, this is a
// temporary solution until we move this into BrowsingContextState,
// along with RenderFrameProxyHost.
page->render_frame_host->frame_tree_node()->set_frame_name_for_activation(
prior_replication_state.unique_name, prior_replication_state.name);
for (auto& it : page->proxy_hosts) {
it.second->set_frame_tree_node(*(target_frame_tree.root()));
}

// Iterate over the root RenderFrameHost's subframes and update the
// associated frame tree. Note that subframe proxies don't need their
// FrameTrees independently updated, since their FrameTreeNodes don't
// change, and FrameTree references in those FrameTreeNodes will be updated
// through RenderFrameHosts.
//
// TODO(https://crbug.com/1199693): Need to investigate if and how
// pending delete RenderFrameHost objects should be handled if prerendering
// runs all of the unload handlers; they are not currently handled here.
// This is because pending delete RenderFrameHosts can still receive and
// process some messages while the RenderFrameHost FrameTree and
// FrameTreeNode are stale.
for (FrameTreeNode* subframe_node : subframe_nodes) {
subframe_node->SetFrameTree(target_frame_tree);
}

page->render_frame_host->ForEachRenderFrameHostIncludingSpeculative(
base::BindRepeating(
[](const WebContentsImpl& web_contents, RenderFrameHostImpl* rfh) {
// The visibility state of the prerendering page has not been
// updated by
// WebContentsImpl::UpdateVisibilityAndNotifyPageAndView(). So
// updates the visibility state using the PageVisibilityState of
// |web_contents|.
rfh->render_view_host()->SetFrameTreeVisibility(
web_contents.GetPageVisibilityState());
},
std::cref(web_contents_)));

frame_tree_->Shutdown();
frame_tree_.reset();

return page;
}

PrerenderHost::LoadingOutcome WaitForLoadCompletionForTesting() {
PrerenderHost::LoadingOutcome status =
PrerenderHost::LoadingOutcome::kLoadingCompleted;
if (!frame_tree_->IsLoadingIncludingInnerFrameTrees())
return status;

base::RunLoop loop;
on_wait_loading_finished_ =
base::BindOnce(&PrerenderHost::PageHolder::FinishWaitingForTesting,
loop.QuitClosure(), &status);
loop.Run();
return status;
}

FrameTree* frame_tree() { return frame_tree_.get(); }

private:
static void FinishWaitingForTesting(base::OnceClosure on_close, // IN-TEST
PrerenderHost::LoadingOutcome* result,
PrerenderHost::LoadingOutcome status) {
*result = status;
std::move(on_close).Run();
}

// WebContents where this prerenderer is embedded.
WebContentsImpl& web_contents_;

// Used for testing, this closure is only set when waiting a page to be
// either loaded for pre-rendering. |frame_tree_| provides us with a trigger
// for when the page is loaded.
base::OnceCallback<void(PrerenderHost::LoadingOutcome)>
on_wait_loading_finished_;

// Frame tree created for the prerenderer to load the page and prepare it for
// a future activation. During activation, the prerendered page will be taken
// out from |frame_tree_| and moved over to |web_contents_|'s primary frame
// tree, while |frame_tree_| will be deleted.
std::unique_ptr<FrameTree> frame_tree_;
};

PrerenderHost::PrerenderHost(const PrerenderAttributes& attributes,
WebContents& web_contents,
PreloadingAttemptImpl* attempt)
Expand Down Expand Up @@ -842,7 +575,7 @@ void PrerenderHost::RecordFinalStatus(base::PassKey<PrerenderHostRegistry>,
}

void PrerenderHost::CreatePageHolder(WebContentsImpl& web_contents) {
page_holder_ = std::make_unique<PageHolder>(web_contents);
page_holder_ = std::make_unique<PrerenderPageHolder>(web_contents);
frame_tree_node_id_ =
page_holder_->frame_tree()->root()->frame_tree_node_id();
}
Expand Down
5 changes: 2 additions & 3 deletions content/browser/preloading/prerender/prerender_host.h
Expand Up @@ -28,6 +28,7 @@ namespace content {

class FrameTree;
class PrerenderHostRegistry;
class PrerenderPageHolder;
class RenderFrameHostImpl;
class WebContentsImpl;

Expand Down Expand Up @@ -197,8 +198,6 @@ class CONTENT_EXPORT PrerenderHost : public WebContentsObserver {
}

private:
class PageHolder;

// Records the status to UMA and UKM. `initiator_ukm_id` represents the page
// that starts prerendering and `prerendered_ukm_id` represents the
// prerendered page. `prerendered_ukm_id` is valid after the page is
Expand Down Expand Up @@ -236,7 +235,7 @@ class CONTENT_EXPORT PrerenderHost : public WebContentsObserver {

absl::optional<FinalStatus> final_status_;

std::unique_ptr<PageHolder> page_holder_;
std::unique_ptr<PrerenderPageHolder> page_holder_;

base::ObserverList<Observer> observers_;

Expand Down

0 comments on commit 083d224

Please sign in to comment.