#include "content/browser/preloading/prerender/prerender_subframe_navigation_throttle.h"
#include "base/memory/ptr_util.h"
#include "content/browser/preloading/prerender/prerender_features.h"
#include "content/browser/preloading/prerender/prerender_final_status.h"
#include "content/browser/preloading/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/renderer_host/render_frame_host_delegate.h"
#include "content/public/browser/navigation_handle.h"
#include "url/origin.h"
namespace content {
void PrerenderSubframeNavigationThrottle::MaybeCreateAndAdd(
NavigationThrottleRegistry& registry) {
auto* navigation_request =
NavigationRequest::From(®istry.GetNavigationHandle());
FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
if (frame_tree_node->IsMainFrame() ||
!frame_tree_node->frame_tree().is_prerendering()) {
return;
}
registry.AddThrottle(
base::WrapUnique(new PrerenderSubframeNavigationThrottle(registry)));
}
PrerenderSubframeNavigationThrottle::PrerenderSubframeNavigationThrottle(
NavigationThrottleRegistry& registry)
: NavigationThrottle(registry),
prerender_root_ftn_id_(
NavigationRequest::From(®istry.GetNavigationHandle())
->frame_tree_node()
->frame_tree()
.root()
->frame_tree_node_id()) {}
PrerenderSubframeNavigationThrottle::~PrerenderSubframeNavigationThrottle() =
default;
const char* PrerenderSubframeNavigationThrottle::GetNameForLogging() {
return "PrerenderSubframeNavigationThrottle";
}
NavigationThrottle::ThrottleCheckResult
PrerenderSubframeNavigationThrottle::WillStartRequest() {
return WillStartOrRedirectRequest();
}
NavigationThrottle::ThrottleCheckResult
PrerenderSubframeNavigationThrottle::WillRedirectRequest() {
return WillStartOrRedirectRequest();
}
NavigationThrottle::ThrottleCheckResult
PrerenderSubframeNavigationThrottle::WillProcessResponse() {
auto* navigation_request = NavigationRequest::From(navigation_handle());
FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
if (!frame_tree_node->frame_tree().is_prerendering()) {
return NavigationThrottle::PROCEED;
}
if (navigation_handle()->IsDownload()) {
PrerenderHostRegistry* prerender_host_registry =
frame_tree_node->current_frame_host()
->delegate()
->GetPrerenderHostRegistry();
prerender_host_registry->CancelHost(
frame_tree_node->frame_tree().root()->frame_tree_node_id(),
PrerenderFinalStatus::kDownload);
return CANCEL;
}
if (!navigation_request->response_should_be_rendered()) {
return PROCEED;
}
RenderFrameHostImpl* rfhi = frame_tree_node->frame_tree().GetMainFrame();
const url::Origin& main_origin = rfhi->GetLastCommittedOrigin();
if (!main_origin.IsSameOriginWith(
navigation_request->GetOriginToCommit().value())) {
return DecidePolicyForCrossOriginSubframeNavigation(*frame_tree_node);
}
return PROCEED;
}
void PrerenderSubframeNavigationThrottle::OnActivated() {
CHECK(!NavigationRequest::From(navigation_handle())
->frame_tree_node()
->frame_tree()
.is_prerendering());
Observe(navigation_handle()->GetWebContents());
}
void PrerenderSubframeNavigationThrottle::DidFinishNavigation(
NavigationHandle* nav_handle) {
auto* finished_navigation = NavigationRequest::From(nav_handle);
if (finished_navigation->prerender_frame_tree_node_id() !=
prerender_root_ftn_id_) {
return;
}
Observe(nullptr);
if (!finished_navigation->HasCommitted()) {
return;
}
if (!is_deferred_) {
return;
}
is_deferred_ = false;
Resume();
}
NavigationThrottle::ThrottleCheckResult
PrerenderSubframeNavigationThrottle::WillCommitWithoutUrlLoader() {
auto* navigation_request = NavigationRequest::From(navigation_handle());
if (navigation_request->GetUrlInfo().is_sandboxed) {
FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
CHECK(!frame_tree_node->IsMainFrame());
return DecidePolicyForCrossOriginSubframeNavigation(*frame_tree_node);
}
return NavigationThrottle::PROCEED;
}
NavigationThrottle::ThrottleCheckResult PrerenderSubframeNavigationThrottle::
DecidePolicyForCrossOriginSubframeNavigation(
const FrameTreeNode& frame_tree_node) {
CHECK(frame_tree_node.frame_tree().is_prerendering());
CHECK(!frame_tree_node.IsMainFrame());
PrerenderHostRegistry* registry = frame_tree_node.current_frame_host()
->delegate()
->GetPrerenderHostRegistry();
PrerenderHost* prerender_host =
registry->FindNonReservedHostById(prerender_root_ftn_id_);
if (!prerender_host) {
return NavigationThrottle::CANCEL;
}
if (prerender_host->AllowCrossOriginSubframeNavigation()) {
return NavigationThrottle::PROCEED;
}
CHECK(!observation_.IsObserving());
observation_.Observe(prerender_host);
CHECK(observation_.IsObservingSource(prerender_host));
is_deferred_ = true;
return NavigationThrottle::DEFER;
}
void PrerenderSubframeNavigationThrottle::OnHostDestroyed(
PrerenderFinalStatus final_status) {
observation_.Reset();
}
NavigationThrottle::ThrottleCheckResult
PrerenderSubframeNavigationThrottle::WillStartOrRedirectRequest() {
auto* navigation_request = NavigationRequest::From(navigation_handle());
FrameTreeNode* frame_tree_node = navigation_request->frame_tree_node();
CHECK(!frame_tree_node->IsMainFrame());
if (!frame_tree_node->frame_tree().is_prerendering()) {
return NavigationThrottle::PROCEED;
}
RenderFrameHostImpl* rfhi = frame_tree_node->frame_tree().GetMainFrame();
const url::Origin& main_origin = rfhi->GetLastCommittedOrigin();
if (!main_origin.IsSameOriginWith(navigation_handle()->GetURL())) {
return DecidePolicyForCrossOriginSubframeNavigation(*frame_tree_node);
}
return NavigationThrottle::PROCEED;
}
}