blob: 79d4ecdf9e4f81b8520544199cec93d27bafbebb [file] [log] [blame]
// Copyright 2015 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/frame_host/navigation_handle_impl.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "content/browser/appcache/appcache_navigation_handle.h"
#include "content/browser/appcache/appcache_service_impl.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/frame_host/debug_urls.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/navigator_delegate.h"
#include "content/browser/loader/resource_dispatcher_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_navigation_handle.h"
#include "content/common/frame_messages.h"
#include "content/public/browser/navigation_ui_data.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/child_process_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "net/base/net_errors.h"
#include "services/network/public/cpp/resource_request_body.h"
namespace content {
namespace {
// Use this to get a new unique ID for a NavigationHandle during construction.
// The returned ID is guaranteed to be nonzero (zero is the "no ID" indicator).
int64_t CreateUniqueHandleID() {
static int64_t unique_id_counter = 0;
return ++unique_id_counter;
}
} // namespace
NavigationHandleImpl::NavigationHandleImpl(
NavigationRequest* navigation_request,
const std::vector<GURL>& redirect_chain,
int pending_nav_entry_id,
net::HttpRequestHeaders request_headers,
const Referrer& sanitized_referrer)
: navigation_request_(navigation_request),
net_error_code_(net::OK),
was_redirected_(false),
request_headers_(std::move(request_headers)),
pending_nav_entry_id_(pending_nav_entry_id),
navigation_id_(CreateUniqueHandleID()),
redirect_chain_(redirect_chain),
reload_type_(ReloadType::NONE),
restore_type_(RestoreType::NONE),
weak_factory_(this) {
const GURL& url = navigation_request_->common_params().url;
TRACE_EVENT_ASYNC_BEGIN2("navigation", "NavigationHandle", this,
"frame_tree_node",
frame_tree_node()->frame_tree_node_id(), "url",
url.possibly_invalid_spec());
DCHECK(!navigation_request_->common_params().navigation_start.is_null());
DCHECK(!IsRendererDebugURL(url));
if (redirect_chain_.empty())
redirect_chain_.push_back(url);
// Mirrors the logic in RenderFrameImpl::SendDidCommitProvisionalLoad.
if (navigation_request_->common_params().transition &
ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
// If the page contained a client redirect (meta refresh,
// document.location), set the referrer appropriately.
sanitized_referrer_ =
Referrer(redirect_chain_[0], sanitized_referrer.policy);
} else {
sanitized_referrer_ = sanitized_referrer;
}
// Try to match this with a pending NavigationEntry if possible. Note that
// the NavigationController itself may be gone if this is a navigation inside
// an interstitial and the interstitial is asynchronously deleting itself due
// to its tab closing.
NavigationControllerImpl* nav_controller =
static_cast<NavigationControllerImpl*>(
frame_tree_node()->navigator()->GetController());
if (pending_nav_entry_id_ && nav_controller) {
NavigationEntryImpl* nav_entry =
nav_controller->GetEntryWithUniqueID(pending_nav_entry_id_);
if (!nav_entry &&
nav_controller->GetPendingEntry() &&
nav_controller->GetPendingEntry()->GetUniqueID() ==
pending_nav_entry_id_) {
nav_entry = nav_controller->GetPendingEntry();
}
if (nav_entry) {
reload_type_ = nav_entry->reload_type();
restore_type_ = nav_entry->restore_type();
}
}
#if defined(OS_ANDROID)
navigation_handle_proxy_ = std::make_unique<NavigationHandleProxy>(this);
#endif
if (IsInMainFrame()) {
TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1(
"navigation", "Navigation StartToCommit", this,
navigation_request_->common_params().navigation_start, "Initial URL",
url.spec());
}
if (IsSameDocument()) {
TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this,
"Same document");
}
}
NavigationHandleImpl::~NavigationHandleImpl() {
#if defined(OS_ANDROID)
navigation_handle_proxy_->DidFinish();
#endif
GetDelegate()->DidFinishNavigation(this);
if (IsInMainFrame()) {
TRACE_EVENT_ASYNC_END2("navigation", "Navigation StartToCommit", this,
"URL",
navigation_request_->common_params().url.spec(),
"Net Error Code", net_error_code_);
}
TRACE_EVENT_ASYNC_END0("navigation", "NavigationHandle", this);
}
NavigatorDelegate* NavigationHandleImpl::GetDelegate() const {
return frame_tree_node()->navigator()->GetDelegate();
}
int64_t NavigationHandleImpl::GetNavigationId() {
return navigation_id_;
}
const GURL& NavigationHandleImpl::GetURL() {
return navigation_request_->common_params().url;
}
SiteInstanceImpl* NavigationHandleImpl::GetStartingSiteInstance() {
return navigation_request_->starting_site_instance();
}
bool NavigationHandleImpl::IsInMainFrame() {
return frame_tree_node()->IsMainFrame();
}
bool NavigationHandleImpl::IsParentMainFrame() {
if (frame_tree_node()->parent())
return frame_tree_node()->parent()->IsMainFrame();
return false;
}
bool NavigationHandleImpl::IsRendererInitiated() {
return !navigation_request_->browser_initiated();
}
bool NavigationHandleImpl::WasServerRedirect() {
return was_redirected_;
}
const std::vector<GURL>& NavigationHandleImpl::GetRedirectChain() {
return redirect_chain_;
}
int NavigationHandleImpl::GetFrameTreeNodeId() {
return frame_tree_node()->frame_tree_node_id();
}
RenderFrameHostImpl* NavigationHandleImpl::GetParentFrame() {
if (IsInMainFrame())
return nullptr;
return frame_tree_node()->parent()->current_frame_host();
}
base::TimeTicks NavigationHandleImpl::NavigationStart() {
return navigation_request_->common_params().navigation_start;
}
base::TimeTicks NavigationHandleImpl::NavigationInputStart() {
return navigation_request_->common_params().input_start;
}
bool NavigationHandleImpl::IsPost() {
return navigation_request_->common_params().method == "POST";
}
const scoped_refptr<network::ResourceRequestBody>&
NavigationHandleImpl::GetResourceRequestBody() {
return navigation_request_->common_params().post_data;
}
const Referrer& NavigationHandleImpl::GetReferrer() {
return sanitized_referrer_;
}
bool NavigationHandleImpl::HasUserGesture() {
return navigation_request_->common_params().has_user_gesture;
}
ui::PageTransition NavigationHandleImpl::GetPageTransition() {
return navigation_request_->common_params().transition;
}
const NavigationUIData* NavigationHandleImpl::GetNavigationUIData() {
return navigation_request_->navigation_ui_data();
}
bool NavigationHandleImpl::IsExternalProtocol() {
return !GetContentClient()->browser()->IsHandledURL(GetURL());
}
net::Error NavigationHandleImpl::GetNetErrorCode() {
return net_error_code_;
}
RenderFrameHostImpl* NavigationHandleImpl::GetRenderFrameHost() {
return navigation_request_->render_frame_host();
}
bool NavigationHandleImpl::IsSameDocument() {
return navigation_request_->IsSameDocument();
}
const net::HttpRequestHeaders& NavigationHandleImpl::GetRequestHeaders() {
return request_headers_;
}
void NavigationHandleImpl::RemoveRequestHeader(const std::string& header_name) {
DCHECK(state() == NavigationRequest::PROCESSING_WILL_REDIRECT_REQUEST ||
state() == NavigationRequest::WILL_REDIRECT_REQUEST);
removed_request_headers_.push_back(header_name);
}
void NavigationHandleImpl::SetRequestHeader(const std::string& header_name,
const std::string& header_value) {
DCHECK(state() == NavigationRequest::INITIAL ||
state() == NavigationRequest::PROCESSING_WILL_START_REQUEST ||
state() == NavigationRequest::PROCESSING_WILL_REDIRECT_REQUEST ||
state() == NavigationRequest::WILL_START_REQUEST ||
state() == NavigationRequest::WILL_REDIRECT_REQUEST);
modified_request_headers_.SetHeader(header_name, header_value);
}
const net::HttpResponseHeaders* NavigationHandleImpl::GetResponseHeaders() {
if (response_headers_for_testing_)
return response_headers_for_testing_.get();
return navigation_request_->response()
? navigation_request_->response()->head.headers.get()
: nullptr;
}
net::HttpResponseInfo::ConnectionInfo
NavigationHandleImpl::GetConnectionInfo() {
return navigation_request_->response()
? navigation_request_->response()->head.connection_info
: net::HttpResponseInfo::ConnectionInfo();
}
const base::Optional<net::SSLInfo> NavigationHandleImpl::GetSSLInfo() {
return navigation_request_->ssl_info();
}
bool NavigationHandleImpl::IsWaitingToCommit() {
return state() == NavigationRequest::READY_TO_COMMIT;
}
bool NavigationHandleImpl::HasCommitted() {
return state() == NavigationRequest::DID_COMMIT ||
state() == NavigationRequest::DID_COMMIT_ERROR_PAGE;
}
bool NavigationHandleImpl::IsErrorPage() {
return state() == NavigationRequest::DID_COMMIT_ERROR_PAGE;
}
bool NavigationHandleImpl::HasSubframeNavigationEntryCommitted() {
return navigation_request_->subframe_entry_committed();
}
bool NavigationHandleImpl::DidReplaceEntry() {
return navigation_request_->did_replace_entry();
}
bool NavigationHandleImpl::ShouldUpdateHistory() {
return navigation_request_->should_update_history();
}
const GURL& NavigationHandleImpl::GetPreviousURL() {
return navigation_request_->previous_url();
}
net::IPEndPoint NavigationHandleImpl::GetSocketAddress() {
// This is CANCELING because although the data comes in after
// WILL_PROCESS_RESPONSE, it's possible for the navigation to be cancelled
// after and the caller might want this value.
DCHECK_GE(state(), NavigationRequest::CANCELING);
return navigation_request_->response()
? navigation_request_->response()->head.remote_endpoint
: net::IPEndPoint();
}
void NavigationHandleImpl::RegisterThrottleForTesting(
std::unique_ptr<NavigationThrottle> navigation_throttle) {
navigation_request_->RegisterThrottleForTesting(
std::move(navigation_throttle));
}
#if defined(OS_ANDROID)
base::android::ScopedJavaGlobalRef<jobject>
NavigationHandleImpl::java_navigation_handle() {
return navigation_handle_proxy_->java_navigation_handle();
}
#endif
bool NavigationHandleImpl::IsDeferredForTesting() {
return navigation_request_->IsDeferredForTesting();
}
bool NavigationHandleImpl::WasStartedFromContextMenu() {
return navigation_request_->common_params().started_from_context_menu;
}
const GURL& NavigationHandleImpl::GetSearchableFormURL() {
return navigation_request_->begin_params()->searchable_form_url;
}
const std::string& NavigationHandleImpl::GetSearchableFormEncoding() {
return navigation_request_->begin_params()->searchable_form_encoding;
}
ReloadType NavigationHandleImpl::GetReloadType() {
return reload_type_;
}
RestoreType NavigationHandleImpl::GetRestoreType() {
return restore_type_;
}
const GURL& NavigationHandleImpl::GetBaseURLForDataURL() {
return navigation_request_->common_params().base_url_for_data_url;
}
NavigationData* NavigationHandleImpl::GetNavigationData() {
return navigation_data_.get();
}
void NavigationHandleImpl::RegisterSubresourceOverride(
mojom::TransferrableURLLoaderPtr transferrable_loader) {
if (!transferrable_loader)
return;
navigation_request_->RegisterSubresourceOverride(
std::move(transferrable_loader));
}
const GlobalRequestID& NavigationHandleImpl::GetGlobalRequestID() {
DCHECK_GE(state(), NavigationRequest::PROCESSING_WILL_PROCESS_RESPONSE);
return navigation_request_->request_id();
}
bool NavigationHandleImpl::IsDownload() {
return navigation_request_->is_download();
}
bool NavigationHandleImpl::IsFormSubmission() {
return navigation_request_->begin_params()->is_form_submission;
}
const std::string& NavigationHandleImpl::GetHrefTranslate() {
return navigation_request_->common_params().href_translate;
}
void NavigationHandleImpl::CallResumeForTesting() {
navigation_request_->CallResumeForTesting();
}
const base::Optional<url::Origin>& NavigationHandleImpl::GetInitiatorOrigin() {
return navigation_request_->common_params().initiator_origin;
}
bool NavigationHandleImpl::IsSameProcess() {
return navigation_request_->is_same_process();
}
int NavigationHandleImpl::GetNavigationEntryOffset() {
return navigation_request_->navigation_entry_offset();
}
bool NavigationHandleImpl::IsSignedExchangeInnerResponse() {
return navigation_request_->response()
? navigation_request_->response()
->head.is_signed_exchange_inner_response
: false;
}
bool NavigationHandleImpl::WasResponseCached() {
return navigation_request_->response()
? navigation_request_->response()->head.was_fetched_via_cache
: false;
}
const net::ProxyServer& NavigationHandleImpl::GetProxyServer() {
return proxy_server_;
}
void NavigationHandleImpl::InitServiceWorkerHandle(
ServiceWorkerContextWrapper* service_worker_context) {
service_worker_handle_.reset(
new ServiceWorkerNavigationHandle(service_worker_context));
}
void NavigationHandleImpl::UpdateStateFollowingRedirect(
const GURL& new_referrer_url,
ThrottleChecksFinishedCallback callback) {
// The navigation should not redirect to a "renderer debug" url. It should be
// blocked in NavigationRequest::OnRequestRedirected or in
// ResourceLoader::OnReceivedRedirect.
// Note: the call to GetURL below returns the post-redirect URL.
// See https://crbug.com/728398.
CHECK(!IsRendererDebugURL(GetURL()));
// Update the navigation parameters.
if (!(GetPageTransition() & ui::PAGE_TRANSITION_CLIENT_REDIRECT)) {
sanitized_referrer_.url = new_referrer_url;
sanitized_referrer_ =
Referrer::SanitizeForRequest(GetURL(), sanitized_referrer_);
}
was_redirected_ = true;
redirect_chain_.push_back(GetURL());
navigation_request_->set_handle_state(
NavigationRequest::PROCESSING_WILL_REDIRECT_REQUEST);
#if defined(OS_ANDROID)
navigation_handle_proxy_->DidRedirect();
#endif
complete_callback_ = std::move(callback);
}
void NavigationHandleImpl::RunCompleteCallback(
NavigationThrottle::ThrottleCheckResult result) {
DCHECK(result.action() != NavigationThrottle::DEFER);
ThrottleChecksFinishedCallback callback = std::move(complete_callback_);
complete_callback_.Reset();
if (!complete_callback_for_testing_.is_null())
std::move(complete_callback_for_testing_).Run(result);
if (!callback.is_null())
std::move(callback).Run(result);
// No code after running the callback, as it might have resulted in our
// destruction.
}
} // namespace content