blob: 0e1d2a96db6d548b58402fa0437c3e5c4940b4f9 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/preloading/prerender/prerender_new_tab_handle.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "content/browser/preloading/preloading_attempt_impl.h"
#include "content/browser/preloading/prerender/prerender_host.h"
#include "content/browser/preloading/prerender/prerender_host_registry.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/frame.mojom.h"
#include "content/public/browser/web_contents_delegate.h"
namespace content {
PrerenderNewTabHandle::PrerenderNewTabHandle(
const PrerenderAttributes& attributes,
BrowserContext& browser_context)
: attributes_(attributes), web_contents_create_params_(&browser_context) {
CHECK(base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab));
CHECK(!attributes.IsBrowserInitiated());
auto* initiator_render_frame_host = RenderFrameHostImpl::FromFrameToken(
attributes_.initiator_process_id,
attributes_.initiator_frame_token.value());
// Create a new WebContents for prerendering in a new tab.
// TODO(crbug.com/1350676): Pass the same creation parameters as
// WebContentsImpl::CreateNewWindow(). Also, set SessionStorageNamespace.
web_contents_create_params_.opener_render_process_id =
initiator_render_frame_host->GetProcess()->GetID();
web_contents_create_params_.opener_render_frame_id =
initiator_render_frame_host->GetRoutingID();
web_contents_create_params_.opener_suppressed = true;
// Set the visibility of the prerendering WebContents to HIDDEN until
// prerender activation.
web_contents_create_params_.initially_hidden = true;
// TODO(crbug.com/1350676): Consider sharing a pre-created WebContents
// instance among multiple new-tab-prerenders as an optimization.
web_contents_ = base::WrapUnique(static_cast<WebContentsImpl*>(
WebContents::Create(web_contents_create_params_).release()));
// The delegate is swapped with a proper one on activation.
web_contents_delegate_ =
GetContentClient()->browser()->CreatePrerenderWebContentsDelegate();
web_contents_delegate_->PrerenderWebContentsCreated(web_contents_.get());
web_contents_->SetDelegate(web_contents_delegate_.get());
// The prerendering WebContents is not visible until activation but should
// have a valid initial empty primary page. This condition is important as
// WebContentsObservers attached to the prerendering WebContents may assume
// there is the primary page and access it during prerendering.
CHECK_EQ(web_contents_->GetVisibility(), Visibility::HIDDEN);
CHECK(web_contents_->GetPrimaryMainFrame());
CHECK(web_contents_->GetPrimaryMainFrame()->is_initial_empty_document());
}
PrerenderNewTabHandle::~PrerenderNewTabHandle() {
if (web_contents_)
web_contents_->SetDelegate(nullptr);
}
int PrerenderNewTabHandle::StartPrerendering(
PreloadingPredictor preloading_predictor) {
CHECK(web_contents_);
CHECK(attributes_.initiator_web_contents);
// Create new PreloadingAttempt and pass all the values corresponding to
// this prerendering attempt.
auto* preloading_data =
PreloadingData::GetOrCreateForWebContents(web_contents_.get());
PreloadingURLMatchCallback same_url_matcher =
PreloadingData::GetSameURLMatcher(attributes_.prerendering_url);
ukm::SourceId triggered_primary_page_source_id =
attributes_.initiator_web_contents->GetPrimaryMainFrame()
->GetPageUkmSourceId();
auto* preloading_attempt =
static_cast<PreloadingAttemptImpl*>(preloading_data->AddPreloadingAttempt(
preloading_predictor, PreloadingType::kPrerender,
std::move(same_url_matcher), triggered_primary_page_source_id));
CHECK(attributes_.eagerness.has_value());
preloading_attempt->SetSpeculationEagerness(attributes_.eagerness.value());
prerender_host_id_ = GetPrerenderHostRegistry().CreateAndStartHost(
attributes_, preloading_attempt);
return prerender_host_id_;
}
void PrerenderNewTabHandle::CancelPrerendering(
const PrerenderCancellationReason& reason) {
GetPrerenderHostRegistry().CancelHost(prerender_host_id_, reason);
}
std::unique_ptr<WebContentsImpl>
PrerenderNewTabHandle::TakeWebContentsIfAvailable(
const mojom::CreateNewWindowParams& create_new_window_params,
const WebContents::CreateParams& web_contents_create_params) {
PrerenderHost* host =
GetPrerenderHostRegistry().FindNonReservedHostById(prerender_host_id_);
if (!host) {
// The host was already canceled, for example, due to disallowed feature
// usage in the prerendered page, while this PrerenderNewTabHandle is still
// alive.
return nullptr;
}
if (host->GetInitialUrl() != create_new_window_params.target_url) {
// The host is not eligible for the target URL.
return nullptr;
}
// Verify the opener frame is the same with the frame that triggered
// prerendering.
if (web_contents_create_params_.opener_render_process_id !=
web_contents_create_params.opener_render_process_id) {
return nullptr;
}
if (web_contents_create_params_.opener_render_frame_id !=
web_contents_create_params.opener_render_frame_id) {
return nullptr;
}
// TODO(crbug.com/1350676): Consider supporting activation for non-empty
// `main_frame_name`.
CHECK(web_contents_create_params_.main_frame_name.empty());
if (web_contents_create_params_.main_frame_name !=
web_contents_create_params.main_frame_name) {
return nullptr;
}
// TODO(crbug.com/1350676): Compare other parameters on CreateNewWindowParams
// and WebContents::CreateParams. Also, we could have some guard to make sure
// that parameters newly added to WebContents::CreateParams are accordingly
// handled here with an approach similar to SameSizeAsDocumentLoader.
CHECK(web_contents_);
web_contents_->SetDelegate(nullptr);
return std::move(web_contents_);
}
PrerenderHostRegistry& PrerenderNewTabHandle::GetPrerenderHostRegistry() {
CHECK(web_contents_);
return *web_contents_->GetPrerenderHostRegistry();
}
} // namespace content