blob: b00c81757a3fb46cdeb13dd32083459676b1e03e [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/prerender/prerender_navigation_throttle.h"
#include "content/browser/prerender/prerender_host.h"
#include "content/browser/prerender/prerender_host_registry.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/storage_partition_impl.h"
#include "third_party/blink/public/common/features.h"
namespace content {
PrerenderNavigationThrottle::~PrerenderNavigationThrottle() = default;
// static
std::unique_ptr<PrerenderNavigationThrottle>
PrerenderNavigationThrottle::MaybeCreateThrottleFor(
NavigationHandle* navigation_handle) {
auto* navigation_request = NavigationRequest::From(navigation_handle);
FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
if (!blink::features::IsPrerender2Enabled() ||
!frame_tree_node->IsMainFrame() ||
!frame_tree_node->frame_tree()->is_prerendering()) {
return nullptr;
}
return base::WrapUnique(new PrerenderNavigationThrottle(navigation_handle));
}
const char* PrerenderNavigationThrottle::GetNameForLogging() {
return "PrerenderNavigationThrottle";
}
NavigationThrottle::ThrottleCheckResult
PrerenderNavigationThrottle::WillStartRequest() {
return WillStartOrRedirectRequest(/*is_redirection=*/false);
}
NavigationThrottle::ThrottleCheckResult
PrerenderNavigationThrottle::WillRedirectRequest() {
return WillStartOrRedirectRequest(/*is_redirection=*/true);
}
PrerenderNavigationThrottle::PrerenderNavigationThrottle(
NavigationHandle* navigation_handle)
: NavigationThrottle(navigation_handle) {}
NavigationThrottle::ThrottleCheckResult
PrerenderNavigationThrottle::WillStartOrRedirectRequest(bool is_redirection) {
DCHECK(blink::features::IsPrerender2Enabled());
// Take the root frame tree node of the prerendering page.
auto* navigation_request = NavigationRequest::From(navigation_handle());
FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
DCHECK(frame_tree_node->IsMainFrame());
DCHECK(frame_tree_node->frame_tree()->is_prerendering());
// Get the prerender host registry.
auto* storage_partition_impl = static_cast<StoragePartitionImpl*>(
frame_tree_node->current_frame_host()->GetStoragePartition());
PrerenderHostRegistry* prerender_host_registry =
storage_partition_impl->GetPrerenderHostRegistry();
// Disallow navigation from a prerendering page and cancel prerendering.
RenderFrameHostImpl* initiator_render_frame_host_impl =
navigation_request->GetInitiatorFrameToken().has_value()
? RenderFrameHostImpl::FromFrameToken(
navigation_request->GetInitiatorProcessID(),
navigation_request->GetInitiatorFrameToken().value())
: nullptr;
if (initiator_render_frame_host_impl &&
initiator_render_frame_host_impl->frame_tree()->is_prerendering()) {
prerender_host_registry->AbandonHostAsync(
frame_tree_node->frame_tree_node_id(),
PrerenderHost::FinalStatus::kMainFrameNavigation);
// TODO(https://crbug.com/1194414): Handle the case the prerendering page
// is reserved for activation, and AbandonHostAsync() could not do nothing
// here.
return CANCEL;
}
// Allow only HTTP(S) schemes.
// https://jeremyroman.github.io/alternate-loading-modes/#no-bad-navs
GURL prerendering_url = navigation_handle()->GetURL();
if (!prerendering_url.SchemeIsHTTPOrHTTPS()) {
prerender_host_registry->AbandonHostAsync(
frame_tree_node->frame_tree_node_id(),
is_redirection ? PrerenderHost::FinalStatus::kInvalidSchemeRedirect
: PrerenderHost::FinalStatus::kInvalidSchemeNavigation);
return CANCEL;
}
// Take the prerender host of the prerendering page.
const PrerenderHost* prerender_host = prerender_host_registry->FindHostById(
frame_tree_node->frame_tree_node_id());
DCHECK(prerender_host);
// Cancel prerendering if this is cross-origin prerendering, cross-origin
// redirection during prerendering, or cross-origin navigation from a
// prerendered page.
url::Origin prerendering_origin = url::Origin::Create(prerendering_url);
if (prerendering_origin != prerender_host->initiator_origin()) {
// Asynchronously abandon the prerender host so that the navigation request
// and the render frame tree indirectly owned by the prerender host can
// outlive the current callstack.
prerender_host_registry->AbandonHostAsync(
frame_tree_node->frame_tree_node_id(),
is_redirection ? PrerenderHost::FinalStatus::kCrossOriginRedirect
: PrerenderHost::FinalStatus::kCrossOriginNavigation);
// TODO(https://crbug.com/1176120): Fallback to NoStatePrefetch.
return CANCEL;
}
return PROCEED;
}
} // namespace content