diff options
author | clamy <clamy@chromium.org> | 2015-09-21 19:18:53 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-09-22 02:19:35 +0000 |
commit | 394057986ff5d00a841a976f3bd8d599603acaa1 (patch) | |
tree | 3cf339bca77629de8a2d9a5d00e1cc793c9020f2 | |
parent | d3090238be5465186fb991d306b4911c407bc6b8 (diff) | |
download | chromium_src-394057986ff5d00a841a976f3bd8d599603acaa1.zip chromium_src-394057986ff5d00a841a976f3bd8d599603acaa1.tar.gz chromium_src-394057986ff5d00a841a976f3bd8d599603acaa1.tar.bz2 |
Add a NavigationThrottle to the public content/ interface
This CL adds a NavigationThrottle class to the public content/ interface. A NavigationThrottle is used to control the flow of navigations. It lives entirely on the UI thread. Eventually, all components that want the functionality of a ResourceThrottle for navigations should transition to a NavigationThrottle: the new architecture for navigations (browser-side navigation) will not support ResourceThrottles for main resources load. See https://docs.google.com/document/d/1ICLLQoC9EsZ-bWH4ZKRhPCIoZKn6pOj02SlGl6SKH6Y/edit?pli=1#heading=h.fmxjmgvbgg7x for the design doc.
This Cl also transition the InterceptNavigationresourceThrottle to the Navigationthrottle model.
BUG=504347
Review URL: https://codereview.chromium.org/1269813002
Cr-Commit-Position: refs/heads/master@{#350092}
36 files changed, 1025 insertions, 820 deletions
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc index 759c844..ab14d49 100644 --- a/android_webview/browser/aw_content_browser_client.cc +++ b/android_webview/browser/aw_content_browser_client.cc @@ -25,10 +25,13 @@ #include "base/command_line.h" #include "base/path_service.h" #include "components/cdm/browser/cdm_message_filter_android.h" +#include "components/navigation_interception/intercept_navigation_delegate.h" #include "content/public/browser/access_token_store.h" #include "content/public/browser/browser_message_filter.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/client_certificate_delegate.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" @@ -487,6 +490,22 @@ void AwContentBrowserClient::OverrideWebkitPrefs( content::WebContents::FromRenderViewHost(rvh), web_prefs); } +ScopedVector<content::NavigationThrottle> +AwContentBrowserClient::CreateThrottlesForNavigation( + content::NavigationHandle* navigation_handle) { + ScopedVector<content::NavigationThrottle> throttles; + if (navigation_handle->IsInMainFrame() || + (!navigation_handle->GetURL().SchemeIs(url::kHttpScheme) && + !navigation_handle->GetURL().SchemeIs(url::kHttpsScheme) && + !navigation_handle->GetURL().SchemeIs(url::kAboutScheme))) { + throttles.push_back( + navigation_interception::InterceptNavigationDelegate::CreateThrottleFor( + navigation_handle) + .Pass()); + } + return throttles.Pass(); +} + #if defined(VIDEO_HOLE) content::ExternalVideoSurfaceContainer* AwContentBrowserClient::OverrideCreateExternalVideoSurfaceContainer( diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h index d811cf2..a3a814e 100644 --- a/android_webview/browser/aw_content_browser_client.h +++ b/android_webview/browser/aw_content_browser_client.h @@ -142,6 +142,8 @@ class AwContentBrowserClient : public content::ContentBrowserClient { std::map<int, base::MemoryMappedFile::Region>* regions) override; void OverrideWebkitPrefs(content::RenderViewHost* rvh, content::WebPreferences* web_prefs) override; + ScopedVector<content::NavigationThrottle> CreateThrottlesForNavigation( + content::NavigationHandle* navigation_handle) override; #if defined(VIDEO_HOLE) content::ExternalVideoSurfaceContainer* OverrideCreateExternalVideoSurfaceContainer( diff --git a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc index 1d9e585..ed7c52b 100644 --- a/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc +++ b/android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.cc @@ -221,14 +221,6 @@ void AwResourceDispatcherHostDelegate::RequestBeginning( throttles->push_back(new IoThreadClientThrottle( request_info->GetChildID(), request_info->GetRenderFrameID(), request)); - if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME || - (resource_type == content::RESOURCE_TYPE_SUB_FRAME && - !request->url().SchemeIs(url::kHttpScheme) && - !request->url().SchemeIs(url::kHttpsScheme) && - !request->url().SchemeIs(url::kAboutScheme))) { - throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor( - request)); - } if (resource_type != content::RESOURCE_TYPE_MAIN_FRAME) InterceptNavigationDelegate::UpdateUserGestureCarryoverInfo(request); } diff --git a/chrome/browser/apps/app_url_redirector.cc b/chrome/browser/apps/app_url_redirector.cc index cf15169..6396293 100644 --- a/chrome/browser/apps/app_url_redirector.cc +++ b/chrome/browser/apps/app_url_redirector.cc @@ -9,22 +9,18 @@ #include "base/logging.h" #include "chrome/browser/prerender/prerender_contents.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/profiles/profile_io_data.h" #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h" -#include "components/navigation_interception/intercept_navigation_resource_throttle.h" +#include "components/navigation_interception/intercept_navigation_throttle.h" #include "components/navigation_interception/navigation_params.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/resource_request_info.h" -#include "content/public/browser/resource_throttle.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" -#include "extensions/browser/info_map.h" +#include "extensions/browser/extension_registry.h" #include "extensions/common/extension.h" #include "extensions/common/extension_set.h" #include "net/url_request/url_request.h" using content::BrowserThread; -using content::ResourceRequestInfo; using content::WebContents; using extensions::Extension; using extensions::UrlHandlers; @@ -55,8 +51,8 @@ bool LaunchAppWithUrl( } // These are guaranteed by CreateThrottleFor below. - DCHECK(!params.is_post()); DCHECK(UrlHandlers::CanExtensionHandleUrl(app.get(), params.url())); + DCHECK(!params.is_post()); Profile* profile = Profile::FromBrowserContext(source->GetBrowserContext()); @@ -73,57 +69,54 @@ bool LaunchAppWithUrl( } // namespace // static -content::ResourceThrottle* -AppUrlRedirector::MaybeCreateThrottleFor(net::URLRequest* request, - ProfileIOData* profile_io_data) { - DVLOG(1) << "Considering URL for redirection: " - << request->method() << " " << request->url().spec(); +scoped_ptr<content::NavigationThrottle> +AppUrlRedirector::MaybeCreateThrottleFor(content::NavigationHandle* handle) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DVLOG(1) << "Considering URL for redirection: " << handle->GetURL().spec(); + + content::BrowserContext* browser_context = + handle->GetWebContents()->GetBrowserContext(); + DCHECK(browser_context); // Support only GET for now. - if (request->method() != "GET") { + if (handle->IsPost()) { DVLOG(1) << "Skip redirection: method is not GET"; - return NULL; + return nullptr; } - if (!request->url().SchemeIsHTTPOrHTTPS()) { + if (!handle->GetURL().SchemeIsHTTPOrHTTPS()) { DVLOG(1) << "Skip redirection: scheme is not HTTP or HTTPS"; - return NULL; - } - - // The user has indicated that a URL should be force downloaded. Turn off - // URL redirection in this case. - if (ResourceRequestInfo::ForRequest(request)->IsDownload()) { - DVLOG(1) << "Skip redirection: request is a forced download"; - return NULL; + return nullptr; } // Never redirect URLs to apps in incognito. Technically, apps are not // supported in incognito, but that may change in future. // See crbug.com/240879, which tracks incognito support for v2 apps. - if (profile_io_data->IsOffTheRecord()) { + Profile* profile = Profile::FromBrowserContext(browser_context); + if (profile->GetProfileType() == Profile::INCOGNITO_PROFILE) { DVLOG(1) << "Skip redirection: unsupported in incognito"; - return NULL; + return nullptr; } - const extensions::ExtensionSet& extensions = - profile_io_data->GetExtensionInfoMap()->extensions(); - for (extensions::ExtensionSet::const_iterator iter = extensions.begin(); - iter != extensions.end(); - ++iter) { + const extensions::ExtensionSet& enabled_extensions = + extensions::ExtensionRegistry::Get(browser_context)->enabled_extensions(); + for (extensions::ExtensionSet::const_iterator iter = + enabled_extensions.begin(); + iter != enabled_extensions.end(); ++iter) { const UrlHandlerInfo* handler = - UrlHandlers::FindMatchingUrlHandler(iter->get(), request->url()); + UrlHandlers::FindMatchingUrlHandler(iter->get(), handle->GetURL()); if (handler) { DVLOG(1) << "Found matching app handler for redirection: " << (*iter)->name() << "(" << (*iter)->id() << "):" << handler->id; - return new navigation_interception::InterceptNavigationResourceThrottle( - request, - base::Bind(&LaunchAppWithUrl, - scoped_refptr<const Extension>(*iter), - handler->id)); + return scoped_ptr<content::NavigationThrottle>( + new navigation_interception::InterceptNavigationThrottle( + handle, + base::Bind(&LaunchAppWithUrl, + scoped_refptr<const Extension>(*iter), handler->id))); } } DVLOG(1) << "Skipping redirection: no matching app handler found"; - return NULL; + return nullptr; } diff --git a/chrome/browser/apps/app_url_redirector.h b/chrome/browser/apps/app_url_redirector.h index 331d7b4..629af37 100644 --- a/chrome/browser/apps/app_url_redirector.h +++ b/chrome/browser/apps/app_url_redirector.h @@ -6,24 +6,22 @@ #define CHROME_BROWSER_APPS_APP_URL_REDIRECTOR_H_ #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/navigation_throttle.h" namespace content { -class ResourceThrottle; +class NavigationHandle; +class NavigationThrottle; +class WebContents; } -namespace net { -class URLRequest; -} - -class ProfileIOData; - -// This class creates resource throttles that redirect URLs to apps that -// have a matching URL handler in the 'url_handlers' manifest key. +// This class creates navigation throttles that redirect URLs to apps that have +// a matching URL handler in the 'url_handlers' manifest key. Note that this is +// a UI thread class. class AppUrlRedirector { public: - static content::ResourceThrottle* MaybeCreateThrottleFor( - net::URLRequest* request, - ProfileIOData* profile_io_data); + static scoped_ptr<content::NavigationThrottle> MaybeCreateThrottleFor( + content::NavigationHandle* handle); private: DISALLOW_COPY_AND_ASSIGN(AppUrlRedirector); diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index 25f4aa4..bbe1c07 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -24,6 +24,7 @@ #include "base/thread_task_runner_handle.h" #include "base/threading/sequenced_worker_pool.h" #include "chrome/browser/after_startup_task_utils.h" +#include "chrome/browser/apps/app_url_redirector.h" #include "chrome/browser/browser_about_handler.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_shutdown.h" @@ -120,6 +121,8 @@ #include "content/public/browser/child_process_data.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/client_certificate_delegate.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" @@ -173,6 +176,7 @@ #include "chrome/browser/chrome_browser_main_android.h" #include "chrome/common/descriptors_android.h" #include "components/crash/content/browser/crash_dump_manager_android.h" +#include "components/navigation_interception/intercept_navigation_delegate.h" #include "components/service_tab_launcher/browser/android/service_tab_launcher.h" #include "ui/base/resource/resource_bundle_android.h" #elif defined(OS_POSIX) @@ -2598,6 +2602,34 @@ void ChromeContentBrowserClient::RecordURLMetric(const std::string& metric, } } +ScopedVector<content::NavigationThrottle> +ChromeContentBrowserClient::CreateThrottlesForNavigation( + content::NavigationHandle* handle) { + ScopedVector<content::NavigationThrottle> throttles; +#if defined(OS_ANDROID) + // TODO(davidben): This is insufficient to integrate with prerender properly. + // https://crbug.com/370595 + prerender::PrerenderContents* prerender_contents = + prerender::PrerenderContents::FromWebContents(handle->GetWebContents()); + if (!prerender_contents && handle->IsInMainFrame()) { + throttles.push_back( + navigation_interception::InterceptNavigationDelegate::CreateThrottleFor( + handle) + .Pass()); + } +#else + if (handle->IsInMainFrame()) { + // Redirect some navigations to apps that have registered matching URL + // handlers ('url_handlers' in the manifest). + scoped_ptr<content::NavigationThrottle> url_to_app_throttle = + AppUrlRedirector::MaybeCreateThrottleFor(handle); + if (url_to_app_throttle) + throttles.push_back(url_to_app_throttle.Pass()); + } +#endif + return throttles.Pass(); +} + content::DevToolsManagerDelegate* ChromeContentBrowserClient::GetDevToolsManagerDelegate() { return new ChromeDevToolsManagerDelegate(); diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index a8b6270..1e2e97f 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h @@ -285,6 +285,8 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient { content::WebContents* web_contents) override; void RecordURLMetric(const std::string& metric, const GURL& url) override; + ScopedVector<content::NavigationThrottle> CreateThrottlesForNavigation( + content::NavigationHandle* handle) override; private: friend class DisableWebRtcEncryptionFlagTest; diff --git a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc index aa4705e..b28332e 100644 --- a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc +++ b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.cc @@ -339,25 +339,8 @@ void ChromeResourceDispatcherHostDelegate::RequestBeginning( resource_context); #if defined(OS_ANDROID) - // TODO(davidben): This is insufficient to integrate with prerender properly. - // https://crbug.com/370595 - if (!is_prerendering) { - if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME) { - throttles->push_back( - InterceptNavigationDelegate::CreateThrottleFor(request)); - } else { - InterceptNavigationDelegate::UpdateUserGestureCarryoverInfo(request); - } - } -#else - if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME) { - // Redirect some navigations to apps that have registered matching URL - // handlers ('url_handlers' in the manifest). - content::ResourceThrottle* url_to_app_throttle = - AppUrlRedirector::MaybeCreateThrottleFor(request, io_data); - if (url_to_app_throttle) - throttles->push_back(url_to_app_throttle); - } + if (resource_type != content::RESOURCE_TYPE_MAIN_FRAME) + InterceptNavigationDelegate::UpdateUserGestureCarryoverInfo(request); #endif #if defined(OS_CHROMEOS) diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 8e3d46c..78a981d 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -353,7 +353,7 @@ 'nacl/zygote/nacl_fork_delegate_linux_unittest.cc', ], 'navigation_interception_unittest_sources': [ - 'navigation_interception/intercept_navigation_resource_throttle_unittest.cc', + 'navigation_interception/intercept_navigation_throttle_unittest.cc', ], 'network_hints_unittest_sources': [ 'network_hints/renderer/dns_prefetch_queue_unittest.cc', diff --git a/components/navigation_interception.gypi b/components/navigation_interception.gypi index 3511201..7ccc4cd 100644 --- a/components/navigation_interception.gypi +++ b/components/navigation_interception.gypi @@ -23,8 +23,8 @@ ], 'sources': [ # Note: sources list duplicated in GN build. - 'navigation_interception/intercept_navigation_resource_throttle.cc', - 'navigation_interception/intercept_navigation_resource_throttle.h', + 'navigation_interception/intercept_navigation_throttle.cc', + 'navigation_interception/intercept_navigation_throttle.h', 'navigation_interception/navigation_params.cc', 'navigation_interception/navigation_params.h', ], diff --git a/components/navigation_interception/BUILD.gn b/components/navigation_interception/BUILD.gn index 12456ff..f268510 100644 --- a/components/navigation_interception/BUILD.gn +++ b/components/navigation_interception/BUILD.gn @@ -8,8 +8,8 @@ if (is_android) { source_set("navigation_interception") { sources = [ - "intercept_navigation_resource_throttle.cc", - "intercept_navigation_resource_throttle.h", + "intercept_navigation_throttle.cc", + "intercept_navigation_throttle.h", "navigation_params.cc", "navigation_params.h", ] @@ -49,7 +49,7 @@ if (is_android) { source_set("unit_tests") { testonly = true sources = [ - "intercept_navigation_resource_throttle_unittest.cc", + "intercept_navigation_throttle_unittest.cc", ] deps = [ ":navigation_interception", diff --git a/components/navigation_interception/intercept_navigation_delegate.cc b/components/navigation_interception/intercept_navigation_delegate.cc index ba9053d..11017f3b 100644 --- a/components/navigation_interception/intercept_navigation_delegate.cc +++ b/components/navigation_interception/intercept_navigation_delegate.cc @@ -7,9 +7,10 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/callback.h" -#include "components/navigation_interception/intercept_navigation_resource_throttle.h" +#include "components/navigation_interception/intercept_navigation_throttle.h" #include "components/navigation_interception/navigation_params_android.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_request_info.h" @@ -83,10 +84,12 @@ InterceptNavigationDelegate* InterceptNavigationDelegate::Get( } // static -content::ResourceThrottle* InterceptNavigationDelegate::CreateThrottleFor( - net::URLRequest* request) { - return new InterceptNavigationResourceThrottle( - request, base::Bind(&CheckIfShouldIgnoreNavigationOnUIThread)); +scoped_ptr<content::NavigationThrottle> +InterceptNavigationDelegate::CreateThrottleFor( + content::NavigationHandle* handle) { + return scoped_ptr<content::NavigationThrottle>( + new InterceptNavigationThrottle( + handle, base::Bind(&CheckIfShouldIgnoreNavigationOnUIThread))); } // static diff --git a/components/navigation_interception/intercept_navigation_delegate.h b/components/navigation_interception/intercept_navigation_delegate.h index d84969f..57858fd 100644 --- a/components/navigation_interception/intercept_navigation_delegate.h +++ b/components/navigation_interception/intercept_navigation_delegate.h @@ -12,7 +12,8 @@ class GURL; namespace content { -class ResourceThrottle; +class NavigationHandle; +class NavigationThrottle; class WebContents; } @@ -32,9 +33,8 @@ class NavigationParams; // 1) the Java-side interface implementation must be associated (via the // Associate method) with a WebContents for which URLRequests are to be // intercepted, -// 2) the ResourceThrottle obtained via CreateThrottleFor must be associated -// with the URLRequests in the ResourceDispatcherHostDelegate -// implementation. +// 2) the NavigationThrottle obtained via CreateThrottleFor must be associated +// with the NavigationHandle in the ContentBrowserClient implementation. class InterceptNavigationDelegate : public base::SupportsUserData::Data { public: InterceptNavigationDelegate(JNIEnv* env, jobject jdelegate); @@ -50,10 +50,10 @@ class InterceptNavigationDelegate : public base::SupportsUserData::Data { // can be null. static InterceptNavigationDelegate* Get(content::WebContents* web_contents); - // Creates a InterceptNavigationResourceThrottle that will direct all - // callbacks to the InterceptNavigationDelegate. - static content::ResourceThrottle* CreateThrottleFor( - net::URLRequest* request); + // Creates a InterceptNavigationThrottle that will direct all callbacks to + // the InterceptNavigationDelegate. + static scoped_ptr<content::NavigationThrottle> CreateThrottleFor( + content::NavigationHandle* handle); // Updates information to determine whether to have user gesture carryover or // not. diff --git a/components/navigation_interception/intercept_navigation_resource_throttle.cc b/components/navigation_interception/intercept_navigation_resource_throttle.cc deleted file mode 100644 index 1b0b89f..0000000 --- a/components/navigation_interception/intercept_navigation_resource_throttle.cc +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2012 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 "components/navigation_interception/intercept_navigation_resource_throttle.h" - -#include "components/navigation_interception/navigation_params.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/child_process_security_policy.h" -#include "content/public/browser/render_frame_host.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/browser/resource_context.h" -#include "content/public/browser/resource_controller.h" -#include "content/public/browser/resource_request_info.h" -#include "content/public/browser/web_contents.h" -#include "content/public/common/referrer.h" -#include "net/http/http_response_headers.h" -#include "net/url_request/redirect_info.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_job_factory.h" -#include "net/url_request/url_request.h" -#include "ui/base/page_transition_types.h" - -using content::BrowserThread; -using content::ChildProcessSecurityPolicy; -using ui::PageTransition; -using content::Referrer; -using content::RenderProcessHost; -using content::ResourceRequestInfo; - -namespace navigation_interception { - -namespace { - -void CheckIfShouldIgnoreNavigationOnUIThread( - int render_process_id, - int render_frame_id, - const NavigationParams& navigation_params, - InterceptNavigationResourceThrottle::CheckOnUIThreadCallback - should_ignore_callback, - base::Callback<void(bool)> callback) { - bool should_ignore_navigation = false; - RenderProcessHost* rph = RenderProcessHost::FromID(render_process_id); - if (rph) { - NavigationParams validated_params(navigation_params); - rph->FilterURL(false, &validated_params.url()); - - content::RenderFrameHost* render_frame_host = - content::RenderFrameHost::FromID(render_process_id, render_frame_id); - content::WebContents* web_contents = - content::WebContents::FromRenderFrameHost(render_frame_host); - - if (web_contents) { - should_ignore_navigation = should_ignore_callback.Run(web_contents, - validated_params); - } - } - - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(callback, should_ignore_navigation)); -} - -} // namespace - -InterceptNavigationResourceThrottle::InterceptNavigationResourceThrottle( - net::URLRequest* request, - CheckOnUIThreadCallback should_ignore_callback) - : request_(request), - should_ignore_callback_(should_ignore_callback), - weak_ptr_factory_(this) { -} - -InterceptNavigationResourceThrottle::~InterceptNavigationResourceThrottle() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); -} - -void InterceptNavigationResourceThrottle::WillStartRequest(bool* defer) { - *defer = - CheckIfShouldIgnoreNavigation(request_->url(), request_->method(), false); -} - -void InterceptNavigationResourceThrottle::WillRedirectRequest( - const net::RedirectInfo& redirect_info, - bool* defer) { - *defer = CheckIfShouldIgnoreNavigation(redirect_info.new_url, - redirect_info.new_method, true); -} - -const char* InterceptNavigationResourceThrottle::GetNameForLogging() const { - return "InterceptNavigationResourceThrottle"; -} - -bool InterceptNavigationResourceThrottle::CheckIfShouldIgnoreNavigation( - const GURL& url, - const std::string& method, - bool is_redirect) { - const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); - if (!info) - return false; - - int render_process_id, render_frame_id; - if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_id)) - return false; - - bool is_external_protocol = - !info->GetContext()->GetRequestContext()->job_factory()->IsHandledURL( - url); - NavigationParams navigation_params( - url, - Referrer::SanitizeForRequest( - url, Referrer(GURL(request_->referrer()), info->GetReferrerPolicy())), - info->HasUserGesture(), method == "POST", info->GetPageTransition(), - is_redirect, is_external_protocol, true); - - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - base::Bind( - &CheckIfShouldIgnoreNavigationOnUIThread, - render_process_id, - render_frame_id, - navigation_params, - should_ignore_callback_, - base::Bind( - &InterceptNavigationResourceThrottle::OnResultObtained, - weak_ptr_factory_.GetWeakPtr()))); - - // Defer request while we wait for the UI thread to check if the navigation - // should be ignored. - return true; -} - -void InterceptNavigationResourceThrottle::OnResultObtained( - bool should_ignore_navigation) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (should_ignore_navigation) { - controller()->CancelAndIgnore(); - } else { - controller()->Resume(); - } -} - -} // namespace navigation_interception diff --git a/components/navigation_interception/intercept_navigation_resource_throttle.h b/components/navigation_interception/intercept_navigation_resource_throttle.h deleted file mode 100644 index db3a714..0000000 --- a/components/navigation_interception/intercept_navigation_resource_throttle.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_RESOURCE_THROTTLE_H_ -#define COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_RESOURCE_THROTTLE_H_ - -#include <string> - -#include "base/callback.h" -#include "base/memory/weak_ptr.h" -#include "content/public/browser/resource_throttle.h" - -class GURL; - -namespace content { -class WebContents; -} - -namespace net { -class URLRequest; -} - -namespace navigation_interception { - -class NavigationParams; - -// This class allows the provider of the Callback to selectively ignore top -// level navigations. -class InterceptNavigationResourceThrottle : public content::ResourceThrottle { - public: - typedef base::Callback<bool( - content::WebContents* /* source */, - const NavigationParams& /* navigation_params */)> - CheckOnUIThreadCallback; - - InterceptNavigationResourceThrottle( - net::URLRequest* request, - CheckOnUIThreadCallback should_ignore_callback); - ~InterceptNavigationResourceThrottle() override; - - // content::ResourceThrottle implementation: - void WillStartRequest(bool* defer) override; - void WillRedirectRequest(const net::RedirectInfo& redirect_info, - bool* defer) override; - const char* GetNameForLogging() const override; - - private: - bool CheckIfShouldIgnoreNavigation(const GURL& url, - const std::string& method, - bool is_redirect); - void OnResultObtained(bool should_ignore_navigation); - - net::URLRequest* request_; - CheckOnUIThreadCallback should_ignore_callback_; - base::WeakPtrFactory<InterceptNavigationResourceThrottle> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(InterceptNavigationResourceThrottle); -}; - -} // namespace navigation_interception - -#endif // COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_RESOURCE_THROTTLE_H_ diff --git a/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc b/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc deleted file mode 100644 index 8855f42..0000000 --- a/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc +++ /dev/null @@ -1,481 +0,0 @@ -// Copyright (c) 2012 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 "base/bind.h" -#include "base/bind_helpers.h" -#include "base/memory/scoped_ptr.h" -#include "base/run_loop.h" -#include "base/synchronization/waitable_event.h" -#include "components/navigation_interception/intercept_navigation_resource_throttle.h" -#include "components/navigation_interception/navigation_params.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_frame_host.h" -#include "content/public/browser/render_process_host.h" -#include "content/public/browser/resource_context.h" -#include "content/public/browser/resource_controller.h" -#include "content/public/browser/resource_dispatcher_host.h" -#include "content/public/browser/resource_dispatcher_host_delegate.h" -#include "content/public/browser/resource_request_info.h" -#include "content/public/browser/resource_throttle.h" -#include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_delegate.h" -#include "content/public/test/mock_resource_context.h" -#include "content/public/test/test_renderer_host.h" -#include "net/base/request_priority.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_response_info.h" -#include "net/url_request/redirect_info.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_test_util.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using content::ResourceType; -using testing::_; -using testing::Eq; -using testing::Ne; -using testing::Property; -using testing::Return; - -namespace navigation_interception { - -namespace { - -const char kTestUrl[] = "http://www.test.com/"; -const char kUnsafeTestUrl[] = "about:crash"; - -// The MS C++ compiler complains about not being able to resolve which url() -// method (const or non-const) to use if we use the Property matcher to check -// the return value of the NavigationParams::url() method. -// It is possible to suppress the error by specifying the types directly but -// that results in very ugly syntax, which is why these custom matchers are -// used instead. -MATCHER(NavigationParamsUrlIsTest, "") { - return arg.url() == GURL(kTestUrl); -} - -MATCHER(NavigationParamsUrlIsSafe, "") { - return arg.url() != GURL(kUnsafeTestUrl); -} - -} // namespace - - -// MockInterceptCallbackReceiver ---------------------------------------------- - -class MockInterceptCallbackReceiver { - public: - MOCK_METHOD2(ShouldIgnoreNavigation, - bool(content::WebContents* source, - const NavigationParams& navigation_params)); -}; - -// MockResourceController ----------------------------------------------------- -class MockResourceController : public content::ResourceController { - public: - enum Status { - UNKNOWN, - RESUMED, - CANCELLED - }; - - MockResourceController() - : status_(UNKNOWN) { - } - - Status status() const { return status_; } - - // ResourceController: - void Cancel() override { NOTREACHED(); } - void CancelAndIgnore() override { status_ = CANCELLED; } - void CancelWithError(int error_code) override { NOTREACHED(); } - void Resume() override { - DCHECK(status_ == UNKNOWN); - status_ = RESUMED; - } - - private: - Status status_; -}; - -// TestIOThreadState ---------------------------------------------------------- - -enum RedirectMode { - REDIRECT_MODE_NO_REDIRECT, - REDIRECT_MODE_302, -}; - -class TestIOThreadState { - public: - TestIOThreadState(const GURL& url, - int render_process_id, - int render_frame_id, - const std::string& request_method, - RedirectMode redirect_mode, - MockInterceptCallbackReceiver* callback_receiver) - : resource_context_(&test_url_request_context_), - request_(resource_context_.GetRequestContext()->CreateRequest( - url, - net::DEFAULT_PRIORITY, - NULL /* delegate */)) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (render_process_id != MSG_ROUTING_NONE && - render_frame_id != MSG_ROUTING_NONE) { - content::ResourceRequestInfo::AllocateForTesting( - request_.get(), - content::RESOURCE_TYPE_MAIN_FRAME, - &resource_context_, - render_process_id, - MSG_ROUTING_NONE, - render_frame_id, - true, // is_main_frame - false, // parent_is_main_frame - true, // allow_download - false); // is_async - } - throttle_.reset(new InterceptNavigationResourceThrottle( - request_.get(), - base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation, - base::Unretained(callback_receiver)))); - throttle_->set_controller_for_testing(&throttle_controller_); - request_->set_method(request_method); - - if (redirect_mode == REDIRECT_MODE_302) { - net::HttpResponseInfo& response_info = - const_cast<net::HttpResponseInfo&>(request_->response_info()); - response_info.headers = new net::HttpResponseHeaders( - "Status: 302 Found\0\0"); - } - } - - void ThrottleWillStartRequest(bool* defer) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - throttle_->WillStartRequest(defer); - } - - void ThrottleWillRedirectRequest(const net::RedirectInfo& redirect_info, - bool* defer) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - throttle_->WillRedirectRequest(redirect_info, defer); - } - - bool request_resumed() const { - return throttle_controller_.status() == - MockResourceController::RESUMED; - } - - bool request_cancelled() const { - return throttle_controller_.status() == - MockResourceController::CANCELLED; - } - - private: - net::TestURLRequestContext test_url_request_context_; - content::MockResourceContext resource_context_; - scoped_ptr<net::URLRequest> request_; - scoped_ptr<InterceptNavigationResourceThrottle> throttle_; - MockResourceController throttle_controller_; -}; - -// InterceptNavigationResourceThrottleTest ------------------------------------ - -class InterceptNavigationResourceThrottleTest - : public content::RenderViewHostTestHarness { - public: - InterceptNavigationResourceThrottleTest() - : mock_callback_receiver_(new MockInterceptCallbackReceiver()), - io_thread_state_(NULL) { - } - - void SetUp() override { RenderViewHostTestHarness::SetUp(); } - - void TearDown() override { - if (web_contents()) - web_contents()->SetDelegate(NULL); - - content::BrowserThread::DeleteSoon( - content::BrowserThread::IO, FROM_HERE, io_thread_state_); - - RenderViewHostTestHarness::TearDown(); - } - - void SetIOThreadState(TestIOThreadState* io_thread_state) { - io_thread_state_ = io_thread_state; - } - - void RunThrottleWillStartRequestOnIOThread( - const GURL& url, - const std::string& request_method, - RedirectMode redirect_mode, - int render_process_id, - int render_frame_id, - bool* defer) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - TestIOThreadState* io_thread_state = - new TestIOThreadState(url, render_process_id, render_frame_id, - request_method, redirect_mode, - mock_callback_receiver_.get()); - - SetIOThreadState(io_thread_state); - - if (redirect_mode == REDIRECT_MODE_NO_REDIRECT) { - io_thread_state->ThrottleWillStartRequest(defer); - } else { - // 302 redirects convert POSTs to gets. - net::RedirectInfo redirect_info; - redirect_info.new_url = url; - redirect_info.new_method = "GET"; - io_thread_state->ThrottleWillRedirectRequest(redirect_info, defer); - } - } - - protected: - enum ShouldIgnoreNavigationCallbackAction { - IgnoreNavigation, - DontIgnoreNavigation - }; - - void SetUpWebContentsDelegateAndDrainRunLoop( - ShouldIgnoreNavigationCallbackAction callback_action, - bool* defer) { - ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _)) - .WillByDefault(Return(callback_action == IgnoreNavigation)); - EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(web_contents(), - NavigationParamsUrlIsTest())) - .Times(1); - - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &InterceptNavigationResourceThrottleTest:: - RunThrottleWillStartRequestOnIOThread, - base::Unretained(this), - GURL(kTestUrl), - "GET", - REDIRECT_MODE_NO_REDIRECT, - web_contents()->GetRenderViewHost()->GetProcess()->GetID(), - web_contents()->GetMainFrame()->GetRoutingID(), - base::Unretained(defer))); - - // Wait for the request to finish processing. - base::RunLoop().RunUntilIdle(); - } - - void WaitForPreviouslyScheduledIoThreadWork() { - base::WaitableEvent io_thread_work_done(true, false); - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &base::WaitableEvent::Signal, - base::Unretained(&io_thread_work_done))); - io_thread_work_done.Wait(); - } - - scoped_ptr<MockInterceptCallbackReceiver> mock_callback_receiver_; - TestIOThreadState* io_thread_state_; -}; - -TEST_F(InterceptNavigationResourceThrottleTest, - RequestDeferredAndResumedIfNavigationNotIgnored) { - bool defer = false; - SetUpWebContentsDelegateAndDrainRunLoop(DontIgnoreNavigation, &defer); - - EXPECT_TRUE(defer); - ASSERT_TRUE(io_thread_state_); - EXPECT_TRUE(io_thread_state_->request_resumed()); -} - -TEST_F(InterceptNavigationResourceThrottleTest, - RequestDeferredAndCancelledIfNavigationIgnored) { - bool defer = false; - SetUpWebContentsDelegateAndDrainRunLoop(IgnoreNavigation, &defer); - - EXPECT_TRUE(defer); - ASSERT_TRUE(io_thread_state_); - EXPECT_TRUE(io_thread_state_->request_cancelled()); -} - -TEST_F(InterceptNavigationResourceThrottleTest, - NoCallbackMadeIfContentsDeletedWhileThrottleRunning) { - bool defer = false; - - // The tested scenario is when the WebContents is deleted after the - // ResourceThrottle has finished processing on the IO thread but before the - // UI thread callback has been processed. Since both threads in this test - // are serviced by one message loop, the post order is the execution order. - EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, _)) - .Times(0); - - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &InterceptNavigationResourceThrottleTest:: - RunThrottleWillStartRequestOnIOThread, - base::Unretained(this), - GURL(kTestUrl), - "GET", - REDIRECT_MODE_NO_REDIRECT, - web_contents()->GetRenderViewHost()->GetProcess()->GetID(), - web_contents()->GetMainFrame()->GetRoutingID(), - base::Unretained(&defer))); - - content::BrowserThread::PostTask( - content::BrowserThread::UI, - FROM_HERE, - base::Bind( - &RenderViewHostTestHarness::DeleteContents, - base::Unretained(this))); - - // The WebContents will now be deleted and only after that will the UI-thread - // callback posted by the ResourceThrottle be executed. - base::RunLoop().RunUntilIdle(); - - EXPECT_TRUE(defer); - ASSERT_TRUE(io_thread_state_); - EXPECT_TRUE(io_thread_state_->request_resumed()); -} - -TEST_F(InterceptNavigationResourceThrottleTest, - RequestNotDeferredForRequestNotAssociatedWithARenderView) { - bool defer = false; - - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &InterceptNavigationResourceThrottleTest:: - RunThrottleWillStartRequestOnIOThread, - base::Unretained(this), - GURL(kTestUrl), - "GET", - REDIRECT_MODE_NO_REDIRECT, - MSG_ROUTING_NONE, - MSG_ROUTING_NONE, - base::Unretained(&defer))); - - // Wait for the request to finish processing. - base::RunLoop().RunUntilIdle(); - - EXPECT_FALSE(defer); -} - -TEST_F(InterceptNavigationResourceThrottleTest, - CallbackCalledWithFilteredUrl) { - bool defer = false; - - ON_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe())) - .WillByDefault(Return(false)); - EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe())) - .Times(1); - - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &InterceptNavigationResourceThrottleTest:: - RunThrottleWillStartRequestOnIOThread, - base::Unretained(this), - GURL(kUnsafeTestUrl), - "GET", - REDIRECT_MODE_NO_REDIRECT, - web_contents()->GetRenderViewHost()->GetProcess()->GetID(), - web_contents()->GetMainFrame()->GetRoutingID(), - base::Unretained(&defer))); - - // Wait for the request to finish processing. - base::RunLoop().RunUntilIdle(); -} - -TEST_F(InterceptNavigationResourceThrottleTest, - CallbackIsPostFalseForGet) { - bool defer = false; - - EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, AllOf( - NavigationParamsUrlIsSafe(), - Property(&NavigationParams::is_post, Eq(false))))) - .WillOnce(Return(false)); - - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &InterceptNavigationResourceThrottleTest:: - RunThrottleWillStartRequestOnIOThread, - base::Unretained(this), - GURL(kTestUrl), - "GET", - REDIRECT_MODE_NO_REDIRECT, - web_contents()->GetRenderViewHost()->GetProcess()->GetID(), - web_contents()->GetMainFrame()->GetRoutingID(), - base::Unretained(&defer))); - - // Wait for the request to finish processing. - base::RunLoop().RunUntilIdle(); -} - -TEST_F(InterceptNavigationResourceThrottleTest, - CallbackIsPostTrueForPost) { - bool defer = false; - - EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, AllOf( - NavigationParamsUrlIsSafe(), - Property(&NavigationParams::is_post, Eq(true))))) - .WillOnce(Return(false)); - - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &InterceptNavigationResourceThrottleTest:: - RunThrottleWillStartRequestOnIOThread, - base::Unretained(this), - GURL(kTestUrl), - "POST", - REDIRECT_MODE_NO_REDIRECT, - web_contents()->GetRenderViewHost()->GetProcess()->GetID(), - web_contents()->GetMainFrame()->GetRoutingID(), - base::Unretained(&defer))); - - // Wait for the request to finish processing. - base::RunLoop().RunUntilIdle(); -} - -TEST_F(InterceptNavigationResourceThrottleTest, - CallbackIsPostFalseForPostConvertedToGetBy302) { - bool defer = false; - - EXPECT_CALL(*mock_callback_receiver_, - ShouldIgnoreNavigation(_, AllOf( - NavigationParamsUrlIsSafe(), - Property(&NavigationParams::is_post, Eq(false))))) - .WillOnce(Return(false)); - - content::BrowserThread::PostTask( - content::BrowserThread::IO, - FROM_HERE, - base::Bind( - &InterceptNavigationResourceThrottleTest:: - RunThrottleWillStartRequestOnIOThread, - base::Unretained(this), - GURL(kTestUrl), - "POST", - REDIRECT_MODE_302, - web_contents()->GetRenderViewHost()->GetProcess()->GetID(), - web_contents()->GetMainFrame()->GetRoutingID(), - base::Unretained(&defer))); - - // Wait for the request to finish processing. - base::RunLoop().RunUntilIdle(); -} - -} // namespace navigation_interception diff --git a/components/navigation_interception/intercept_navigation_throttle.cc b/components/navigation_interception/intercept_navigation_throttle.cc new file mode 100644 index 0000000..cc256a1 --- /dev/null +++ b/components/navigation_interception/intercept_navigation_throttle.cc @@ -0,0 +1,49 @@ +// 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 "components/navigation_interception/intercept_navigation_throttle.h" + +#include "components/navigation_interception/navigation_params.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/navigation_handle.h" + +namespace navigation_interception { + +InterceptNavigationThrottle::InterceptNavigationThrottle( + content::NavigationHandle* navigation_handle, + CheckCallback should_ignore_callback) + : content::NavigationThrottle(navigation_handle), + should_ignore_callback_(should_ignore_callback) {} + +InterceptNavigationThrottle::~InterceptNavigationThrottle() {} + +content::NavigationThrottle::ThrottleCheckResult +InterceptNavigationThrottle::WillStartRequest() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + return CheckIfShouldIgnoreNavigation(false); +} + +content::NavigationThrottle::ThrottleCheckResult +InterceptNavigationThrottle::WillRedirectRequest() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + return CheckIfShouldIgnoreNavigation(true); +} + +content::NavigationThrottle::ThrottleCheckResult +InterceptNavigationThrottle::CheckIfShouldIgnoreNavigation(bool is_redirect) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + NavigationParams navigation_params( + navigation_handle()->GetURL(), navigation_handle()->GetReferrer(), + navigation_handle()->HasUserGesture(), navigation_handle()->IsPost(), + navigation_handle()->GetPageTransition(), is_redirect, + navigation_handle()->IsExternalProtocol(), true); + + bool should_ignore_navigation = should_ignore_callback_.Run( + navigation_handle()->GetWebContents(), navigation_params); + return should_ignore_navigation + ? content::NavigationThrottle::CANCEL_AND_IGNORE + : content::NavigationThrottle::PROCEED; +} + +} // namespace navigation_interception diff --git a/components/navigation_interception/intercept_navigation_throttle.h b/components/navigation_interception/intercept_navigation_throttle.h new file mode 100644 index 0000000..0b7f746 --- /dev/null +++ b/components/navigation_interception/intercept_navigation_throttle.h @@ -0,0 +1,51 @@ +// 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. + +#ifndef COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_THROTTLE_H_ +#define COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_THROTTLE_H_ + +#include <string> + +#include "base/callback.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/navigation_throttle.h" + +class GURL; + +namespace content { +class NavigationHandle; +class WebContents; +} + +namespace navigation_interception { + +class NavigationParams; + +// This class allows the provider of the Callback to selectively ignore top +// level navigations. This is a UI thread class. +class InterceptNavigationThrottle : public content::NavigationThrottle { + public: + typedef base::Callback<bool(content::WebContents* /* source */, + const NavigationParams& /* navigation_params */)> + CheckCallback; + + InterceptNavigationThrottle(content::NavigationHandle* navigation_handle, + CheckCallback should_ignore_callback); + ~InterceptNavigationThrottle() override; + + // content::NavigationThrottle implementation: + ThrottleCheckResult WillStartRequest() override; + ThrottleCheckResult WillRedirectRequest() override; + + private: + ThrottleCheckResult CheckIfShouldIgnoreNavigation(bool is_redirect); + + CheckCallback should_ignore_callback_; + + DISALLOW_COPY_AND_ASSIGN(InterceptNavigationThrottle); +}; + +} // namespace navigation_interception + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_THROTTLE_H_ diff --git a/components/navigation_interception/intercept_navigation_throttle_unittest.cc b/components/navigation_interception/intercept_navigation_throttle_unittest.cc new file mode 100644 index 0000000..15e05ef --- /dev/null +++ b/components/navigation_interception/intercept_navigation_throttle_unittest.cc @@ -0,0 +1,165 @@ +// 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 "base/bind.h" +#include "base/bind_helpers.h" +#include "base/memory/scoped_ptr.h" +#include "components/navigation_interception/intercept_navigation_throttle.h" +#include "components/navigation_interception/navigation_params.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/navigation_throttle.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/test_renderer_host.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::NavigationThrottle; +using testing::_; +using testing::Eq; +using testing::Ne; +using testing::Property; +using testing::Return; + +namespace navigation_interception { + +namespace { + +const char kTestUrl[] = "http://www.test.com/"; + +// The MS C++ compiler complains about not being able to resolve which url() +// method (const or non-const) to use if we use the Property matcher to check +// the return value of the NavigationParams::url() method. +// It is possible to suppress the error by specifying the types directly but +// that results in very ugly syntax, which is why these custom matchers are +// used instead. +MATCHER(NavigationParamsUrlIsTest, "") { + return arg.url() == GURL(kTestUrl); +} + +} // namespace + +// MockInterceptCallbackReceiver ---------------------------------------------- + +class MockInterceptCallbackReceiver { + public: + MOCK_METHOD2(ShouldIgnoreNavigation, + bool(content::WebContents* source, + const NavigationParams& navigation_params)); +}; + +// InterceptNavigationThrottleTest ------------------------------------ + +class InterceptNavigationThrottleTest + : public content::RenderViewHostTestHarness { + public: + InterceptNavigationThrottleTest() + : mock_callback_receiver_(new MockInterceptCallbackReceiver()) {} + + NavigationThrottle::ThrottleCheckResult + SimulateWillStart(const GURL& url, const GURL& sanitized_url, bool is_post) { + scoped_ptr<content::NavigationHandle> test_handle = + content::NavigationHandle::CreateNavigationHandleForTesting( + url, true, web_contents()); + test_handle->RegisterThrottleForTesting( + scoped_ptr<NavigationThrottle>( + new InterceptNavigationThrottle( + test_handle.get(), + base::Bind( + &MockInterceptCallbackReceiver::ShouldIgnoreNavigation, + base::Unretained(mock_callback_receiver_.get())))) + .Pass()); + return test_handle->CallWillStartRequestForTesting( + is_post, content::Referrer(), false, ui::PAGE_TRANSITION_LINK, false); + } + + NavigationThrottle::ThrottleCheckResult Simulate302() { + scoped_ptr<content::NavigationHandle> test_handle = + content::NavigationHandle::CreateNavigationHandleForTesting( + GURL(kTestUrl), true, web_contents()); + test_handle->RegisterThrottleForTesting( + scoped_ptr<NavigationThrottle>( + new InterceptNavigationThrottle( + test_handle.get(), + base::Bind( + &MockInterceptCallbackReceiver::ShouldIgnoreNavigation, + base::Unretained(mock_callback_receiver_.get())))) + .Pass()); + test_handle->CallWillStartRequestForTesting( + true, content::Referrer(), false, ui::PAGE_TRANSITION_LINK, false); + return test_handle->CallWillRedirectRequestForTesting(GURL(kTestUrl), false, + GURL(), false); + } + + scoped_ptr<MockInterceptCallbackReceiver> mock_callback_receiver_; +}; + +TEST_F(InterceptNavigationThrottleTest, + RequestDeferredAndResumedIfNavigationNotIgnored) { + ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _)) + .WillByDefault(Return(false)); + EXPECT_CALL( + *mock_callback_receiver_, + ShouldIgnoreNavigation(web_contents(), NavigationParamsUrlIsTest())); + NavigationThrottle::ThrottleCheckResult result = + SimulateWillStart(GURL(kTestUrl), GURL(kTestUrl), false); + + EXPECT_EQ(NavigationThrottle::PROCEED, result); +} + +TEST_F(InterceptNavigationThrottleTest, + RequestDeferredAndCancelledIfNavigationIgnored) { + ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _)) + .WillByDefault(Return(true)); + EXPECT_CALL( + *mock_callback_receiver_, + ShouldIgnoreNavigation(web_contents(), NavigationParamsUrlIsTest())); + NavigationThrottle::ThrottleCheckResult result = + SimulateWillStart(GURL(kTestUrl), GURL(kTestUrl), false); + + EXPECT_EQ(NavigationThrottle::CANCEL_AND_IGNORE, result); +} + +TEST_F(InterceptNavigationThrottleTest, CallbackIsPostFalseForGet) { + EXPECT_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation( + _, AllOf(NavigationParamsUrlIsTest(), + Property(&NavigationParams::is_post, Eq(false))))) + .WillOnce(Return(false)); + + NavigationThrottle::ThrottleCheckResult result = + SimulateWillStart(GURL(kTestUrl), GURL(kTestUrl), false); + + EXPECT_EQ(NavigationThrottle::PROCEED, result); +} + +TEST_F(InterceptNavigationThrottleTest, CallbackIsPostTrueForPost) { + EXPECT_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation( + _, AllOf(NavigationParamsUrlIsTest(), + Property(&NavigationParams::is_post, Eq(true))))) + .WillOnce(Return(false)); + NavigationThrottle::ThrottleCheckResult result = + SimulateWillStart(GURL(kTestUrl), GURL(kTestUrl), true); + + EXPECT_EQ(NavigationThrottle::PROCEED, result); +} + +TEST_F(InterceptNavigationThrottleTest, + CallbackIsPostFalseForPostConvertedToGetBy302) { + EXPECT_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation( + _, AllOf(NavigationParamsUrlIsTest(), + Property(&NavigationParams::is_post, Eq(true))))) + .WillOnce(Return(false)); + EXPECT_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation( + _, AllOf(NavigationParamsUrlIsTest(), + Property(&NavigationParams::is_post, Eq(false))))) + .WillOnce(Return(false)); + NavigationThrottle::ThrottleCheckResult result = Simulate302(); + + EXPECT_EQ(NavigationThrottle::PROCEED, result); +} + +} // namespace navigation_interception diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc index 2c5e773..994a2fc 100644 --- a/content/browser/frame_host/navigation_handle_impl.cc +++ b/content/browser/frame_host/navigation_handle_impl.cc @@ -5,6 +5,8 @@ #include "content/browser/frame_host/navigation_handle_impl.h" #include "content/browser/frame_host/navigator_delegate.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/common/content_client.h" #include "net/url_request/redirect_info.h" namespace content { @@ -12,7 +14,7 @@ namespace content { // static scoped_ptr<NavigationHandleImpl> NavigationHandleImpl::Create( const GURL& url, - const bool is_main_frame, + bool is_main_frame, NavigatorDelegate* delegate) { return scoped_ptr<NavigationHandleImpl>( new NavigationHandleImpl(url, is_main_frame, delegate)); @@ -22,10 +24,14 @@ NavigationHandleImpl::NavigationHandleImpl(const GURL& url, const bool is_main_frame, NavigatorDelegate* delegate) : url_(url), - net_error_code_(net::OK), - state_(DID_START), is_main_frame_(is_main_frame), + is_post_(false), + has_user_gesture_(false), + transition_(ui::PAGE_TRANSITION_LINK), + is_external_protocol_(false), + net_error_code_(net::OK), is_same_page_(false), + state_(INITIAL), is_transferring_(false), delegate_(delegate) { delegate_->DidStartNavigation(this); @@ -35,16 +41,46 @@ NavigationHandleImpl::~NavigationHandleImpl() { delegate_->DidFinishNavigation(this); } -const GURL& NavigationHandleImpl::GetURL() const { +const GURL& NavigationHandleImpl::GetURL() { return url_; } -net::Error NavigationHandleImpl::GetNetErrorCode() const { - return net_error_code_; +bool NavigationHandleImpl::IsInMainFrame() { + return is_main_frame_; } -bool NavigationHandleImpl::IsInMainFrame() const { - return is_main_frame_; +bool NavigationHandleImpl::IsPost() { + CHECK_NE(INITIAL, state_) + << "This accessor should not be called before the request is started."; + return is_post_; +} + +const Referrer& NavigationHandleImpl::GetReferrer() { + CHECK_NE(INITIAL, state_) + << "This accessor should not be called before the request is started."; + return sanitized_referrer_; +} + +bool NavigationHandleImpl::HasUserGesture() { + CHECK_NE(INITIAL, state_) + << "This accessor should not be called before the request is started."; + return has_user_gesture_; +} + +ui::PageTransition NavigationHandleImpl::GetPageTransition() { + CHECK_NE(INITIAL, state_) + << "This accessor should not be called before the request is started."; + return transition_; +} + +bool NavigationHandleImpl::IsExternalProtocol() { + CHECK_NE(INITIAL, state_) + << "This accessor should not be called before the request is started."; + return is_external_protocol_; +} + +net::Error NavigationHandleImpl::GetNetErrorCode() { + return net_error_code_; } bool NavigationHandleImpl::IsSamePage() { @@ -54,14 +90,99 @@ bool NavigationHandleImpl::IsSamePage() { return is_same_page_; } -bool NavigationHandleImpl::HasCommittedDocument() const { +bool NavigationHandleImpl::HasCommittedDocument() { return state_ == DID_COMMIT; } -bool NavigationHandleImpl::HasCommittedErrorPage() const { +bool NavigationHandleImpl::HasCommittedErrorPage() { return state_ == DID_COMMIT_ERROR_PAGE; } +void NavigationHandleImpl::RegisterThrottleForTesting( + scoped_ptr<NavigationThrottle> navigation_throttle) { + throttles_.push_back(navigation_throttle.Pass()); +} + +NavigationThrottle::ThrottleCheckResult +NavigationHandleImpl::CallWillStartRequestForTesting( + bool is_post, + const Referrer& sanitized_referrer, + bool has_user_gesture, + ui::PageTransition transition, + bool is_external_protocol) { + return WillStartRequest(is_post, sanitized_referrer, has_user_gesture, + transition, is_external_protocol); +} + +NavigationThrottle::ThrottleCheckResult +NavigationHandleImpl::CallWillRedirectRequestForTesting( + const GURL& new_url, + bool new_method_is_post, + const GURL& new_referrer_url, + bool new_is_external_protocol) { + return WillRedirectRequest(new_url, new_method_is_post, new_referrer_url, + new_is_external_protocol); +} + +NavigationThrottle::ThrottleCheckResult NavigationHandleImpl::WillStartRequest( + bool is_post, + const Referrer& sanitized_referrer, + bool has_user_gesture, + ui::PageTransition transition, + bool is_external_protocol) { + // Update the navigation parameters. + is_post_ = is_post; + sanitized_referrer_ = sanitized_referrer; + has_user_gesture_ = has_user_gesture; + transition_ = transition; + is_external_protocol_ = is_external_protocol; + + state_ = WILL_SEND_REQUEST; + + // Register the navigation throttles. The ScopedVector returned by + // GetNavigationThrottles is not assigned to throttles_ directly because it + // would overwrite any throttle previously added with + // RegisterThrottleForTesting. + ScopedVector<NavigationThrottle> throttles_to_register = + GetContentClient()->browser()->CreateThrottlesForNavigation(this); + if (throttles_to_register.size() > 0) { + throttles_.insert(throttles_.end(), throttles_to_register.begin(), + throttles_to_register.end()); + throttles_to_register.weak_clear(); + } + + // Notify each throttle of the request. + for (NavigationThrottle* throttle : throttles_) { + NavigationThrottle::ThrottleCheckResult result = + throttle->WillStartRequest(); + if (result == NavigationThrottle::CANCEL_AND_IGNORE) + return NavigationThrottle::CANCEL_AND_IGNORE; + } + return NavigationThrottle::PROCEED; +} + +NavigationThrottle::ThrottleCheckResult +NavigationHandleImpl::WillRedirectRequest(const GURL& new_url, + bool new_method_is_post, + const GURL& new_referrer_url, + bool new_is_external_protocol) { + // Update the navigation parameters. + url_ = new_url; + is_post_ = new_method_is_post; + sanitized_referrer_.url = new_referrer_url; + sanitized_referrer_ = Referrer::SanitizeForRequest(url_, sanitized_referrer_); + is_external_protocol_ = new_is_external_protocol; + + // Have each throttle be notified of the request. + for (NavigationThrottle* throttle : throttles_) { + NavigationThrottle::ThrottleCheckResult result = + throttle->WillRedirectRequest(); + if (result == NavigationThrottle::CANCEL_AND_IGNORE) + return NavigationThrottle::CANCEL_AND_IGNORE; + } + return NavigationThrottle::PROCEED; +} + void NavigationHandleImpl::DidRedirectNavigation(const GURL& new_url) { url_ = new_url; delegate_->DidRedirectNavigation(this); diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h index 861adb0..aebb929 100644 --- a/content/browser/frame_host/navigation_handle_impl.h +++ b/content/browser/frame_host/navigation_handle_impl.h @@ -9,7 +9,9 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "content/common/content_export.h" +#include "content/public/browser/navigation_throttle.h" #include "url/gurl.h" namespace content { @@ -53,18 +55,37 @@ struct NavigationRequestInfo; class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle { public: static scoped_ptr<NavigationHandleImpl> Create(const GURL& url, - const bool is_main_frame, + bool is_main_frame, NavigatorDelegate* delegate); - ~NavigationHandleImpl() override; // NavigationHandle implementation: - const GURL& GetURL() const override; - net::Error GetNetErrorCode() const override; - bool IsInMainFrame() const override; + const GURL& GetURL() override; + bool IsInMainFrame() override; + bool IsPost() override; + const Referrer& GetReferrer() override; + bool HasUserGesture() override; + ui::PageTransition GetPageTransition() override; + bool IsExternalProtocol() override; + net::Error GetNetErrorCode() override; bool IsSamePage() override; - bool HasCommittedDocument() const override; - bool HasCommittedErrorPage() const override; + bool HasCommittedDocument() override; + bool HasCommittedErrorPage() override; + void RegisterThrottleForTesting( + scoped_ptr<NavigationThrottle> navigation_throttle) override; + NavigationThrottle::ThrottleCheckResult CallWillStartRequestForTesting( + bool is_post, + const Referrer& sanitized_referrer, + bool has_user_gesture, + ui::PageTransition transition, + bool is_external_protocol) override; + NavigationThrottle::ThrottleCheckResult CallWillRedirectRequestForTesting( + const GURL& new_url, + bool new_method_is_post, + const GURL& new_referrer_url, + bool new_is_external_protocol) override; + + NavigatorDelegate* delegate() const { return delegate_; } void set_net_error_code(net::Error net_error_code) { net_error_code_ = net_error_code; @@ -79,6 +100,21 @@ class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle { is_transferring_ = is_transferring; } + // Called when the URLRequest will start in the network stack. + NavigationThrottle::ThrottleCheckResult WillStartRequest( + bool is_post, + const Referrer& sanitized_referrer, + bool has_user_gesture, + ui::PageTransition transition, + bool is_external_protocol); + + // Called when the URLRequest will be redirected in the network stack. + NavigationThrottle::ThrottleCheckResult WillRedirectRequest( + const GURL& new_url, + bool new_method_is_post, + const GURL& new_referrer_url, + bool new_is_external_protocol); + // Called when the navigation was redirected. This will update the |url_| and // inform the delegate. void DidRedirectNavigation(const GURL& new_url); @@ -90,7 +126,8 @@ class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle { private: // Used to track the state the navigation is currently in. enum State { - DID_START = 0, + INITIAL = 0, + WILL_SEND_REQUEST, DID_COMMIT, DID_COMMIT_ERROR_PAGE, }; @@ -101,11 +138,18 @@ class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle { // See NavigationHandle for a description of those member variables. GURL url_; - net::Error net_error_code_; - State state_; const bool is_main_frame_; + bool is_post_; + Referrer sanitized_referrer_; + bool has_user_gesture_; + ui::PageTransition transition_; + bool is_external_protocol_; + net::Error net_error_code_; bool is_same_page_; + // The state the navigation is in. + State state_; + // Whether the navigation is in the middle of a transfer. Set to false when // the DidStartProvisionalLoad is received from the new renderer. bool is_transferring_; @@ -114,6 +158,9 @@ class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle { // navigation. NavigatorDelegate* delegate_; + // A list of Throttles registered for this navigation. + ScopedVector<NavigationThrottle> throttles_; + DISALLOW_COPY_AND_ASSIGN(NavigationHandleImpl); }; diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc index a99074f..8247f35 100644 --- a/content/browser/frame_host/navigation_request.cc +++ b/content/browser/frame_host/navigation_request.cc @@ -14,6 +14,7 @@ #include "content/browser/site_instance_impl.h" #include "content/common/resource_request_body.h" #include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/stream_handle.h" #include "content/public/common/content_client.h" #include "net/base/load_flags.h" @@ -179,6 +180,20 @@ bool NavigationRequest::BeginNavigation() { state_ = STARTED; if (ShouldMakeNetworkRequestForURL(common_params_.url)) { + // TODO(clamy): pass the real value for |is_external_protocol| if needed. + NavigationThrottle::ThrottleCheckResult result = + navigation_handle_->WillStartRequest( + begin_params_.method == "POST", + Referrer::SanitizeForRequest(common_params_.url, + common_params_.referrer), + begin_params_.has_user_gesture, common_params_.transition, false); + + // Abort the request if needed. This will destroy the NavigationRequest. + if (result == NavigationThrottle::CANCEL_AND_IGNORE) { + frame_tree_node_->ResetNavigationRequest(false); + return false; + } + loader_ = NavigationURLLoader::Create( frame_tree_node_->navigator()->GetController()->GetBrowserContext(), frame_tree_node_->frame_tree_node_id(), info_.Pass(), this); @@ -209,12 +224,24 @@ void NavigationRequest::TransferNavigationHandleOwnership( void NavigationRequest::OnRequestRedirected( const net::RedirectInfo& redirect_info, const scoped_refptr<ResourceResponse>& response) { - // TODO(davidben): Track other changes from redirects. These are important - // for, e.g., reloads. common_params_.url = redirect_info.new_url; + begin_params_.method = redirect_info.new_method; + common_params_.referrer.url = GURL(redirect_info.new_referrer); + + // TODO(clamy): Have CSP + security upgrade checks here. + // TODO(clamy): Kill the renderer if FilterURL fails? + // TODO(clamy): pass the real value for |is_external_protocol| if needed. + NavigationThrottle::ThrottleCheckResult result = + navigation_handle_->WillRedirectRequest( + common_params_.url, begin_params_.method == "POST", + common_params_.referrer.url, false); + + // Abort the request if needed. This will destroy the NavigationRequest. + if (result == NavigationThrottle::CANCEL_AND_IGNORE) { + frame_tree_node_->ResetNavigationRequest(false); + return; + } - // TODO(davidben): This where prerender and navigation_interceptor should be - // integrated. For now, just always follow all redirects. loader_->FollowRedirect(); navigation_handle_->DidRedirectNavigation(redirect_info.new_url); diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h index a1530b4..b711278 100644 --- a/content/browser/frame_host/navigation_request.h +++ b/content/browser/frame_host/navigation_request.h @@ -164,8 +164,10 @@ class CONTENT_EXPORT NavigationRequest : public NavigationURLLoaderDelegate { // Note: When the navigation is ready to commit, the url in |common_params| // will be set to the final navigation url, obtained after following all // redirects. + // Note: |common_params_| and |begin_params_| are not const as they can be + // modified during redirects. CommonNavigationParams common_params_; - const BeginNavigationParams begin_params_; + BeginNavigationParams begin_params_; const RequestNavigationParams request_params_; const bool browser_initiated_; diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc index b03e232..fa0c049 100644 --- a/content/browser/frame_host/navigator_impl.cc +++ b/content/browser/frame_host/navigator_impl.cc @@ -159,7 +159,7 @@ void NavigatorImpl::DidStartProvisionalLoad( } render_frame_host->SetNavigationHandle( - NavigationHandleImpl::Create(url, is_main_frame, delegate_)); + NavigationHandleImpl::Create(validated_url, is_main_frame, delegate_)); } void NavigatorImpl::DidFailProvisionalLoadWithError( diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 8f6c99c..aed2daa 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc @@ -1359,8 +1359,10 @@ void RenderFrameHostImpl::OnBeginNavigation( scoped_refptr<ResourceRequestBody> body) { CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableBrowserSideNavigation)); + CommonNavigationParams validated_params = common_params; + GetProcess()->FilterURL(false, &validated_params.url); frame_tree_node()->navigator()->OnBeginNavigation( - frame_tree_node(), common_params, begin_params, body); + frame_tree_node(), validated_params, begin_params, body); } void RenderFrameHostImpl::OnDispatchLoad() { diff --git a/content/browser/loader/navigation_resource_throttle.cc b/content/browser/loader/navigation_resource_throttle.cc new file mode 100644 index 0000000..8dd143c --- /dev/null +++ b/content/browser/loader/navigation_resource_throttle.cc @@ -0,0 +1,154 @@ +// 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/loader/navigation_resource_throttle.h" + +#include "base/callback.h" +#include "content/browser/frame_host/navigation_handle_impl.h" +#include "content/browser/frame_host/render_frame_host_impl.h" +#include "content/public/browser/resource_context.h" +#include "content/public/browser/resource_controller.h" +#include "content/public/browser/resource_request_info.h" +#include "content/public/common/referrer.h" +#include "net/url_request/redirect_info.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_job_factory.h" +#include "ui/base/page_transition_types.h" + +namespace content { + +namespace { +typedef base::Callback<void(NavigationThrottle::ThrottleCheckResult)> + UIChecksPerformedCallback; + +void CheckWillStartRequestOnUIThread(UIChecksPerformedCallback callback, + int render_process_id, + int render_frame_host_id, + bool is_post, + const Referrer& sanitized_referrer, + bool has_user_gesture, + ui::PageTransition transition, + bool is_external_protocol) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + NavigationThrottle::ThrottleCheckResult result = NavigationThrottle::PROCEED; + RenderFrameHostImpl* render_frame_host = + RenderFrameHostImpl::FromID(render_process_id, render_frame_host_id); + if (render_frame_host) { + NavigationHandleImpl* navigation_handle = + render_frame_host->navigation_handle(); + if (navigation_handle) { + result = navigation_handle->WillStartRequest(is_post, sanitized_referrer, + has_user_gesture, transition, + is_external_protocol); + } + } + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(callback, result)); +} + +void CheckWillRedirectRequestOnUIThread(UIChecksPerformedCallback callback, + int render_process_id, + int render_frame_host_id, + const GURL& new_url, + bool new_method_is_post, + const GURL& new_referrer_url, + bool new_is_external_protocol) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + NavigationThrottle::ThrottleCheckResult result = NavigationThrottle::PROCEED; + RenderFrameHostImpl* render_frame_host = + RenderFrameHostImpl::FromID(render_process_id, render_frame_host_id); + if (render_frame_host) { + NavigationHandleImpl* navigation_handle = + render_frame_host->navigation_handle(); + if (navigation_handle) { + RenderProcessHost* rph = RenderProcessHost::FromID(render_process_id); + GURL new_validated_url = new_url; + rph->FilterURL(false, &new_validated_url); + result = navigation_handle->WillRedirectRequest( + new_validated_url, new_method_is_post, new_referrer_url, + new_is_external_protocol); + } + } + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(callback, result)); +} +} // namespace + +NavigationResourceThrottle::NavigationResourceThrottle(net::URLRequest* request) + : request_(request), weak_ptr_factory_(this) {} + +NavigationResourceThrottle::~NavigationResourceThrottle() {} + +void NavigationResourceThrottle::WillStartRequest(bool* defer) { + const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); + if (!info) + return; + + int render_process_id, render_frame_id; + if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_id)) + return; + + bool is_external_protocol = + !info->GetContext()->GetRequestContext()->job_factory()->IsHandledURL( + request_->url()); + UIChecksPerformedCallback callback = + base::Bind(&NavigationResourceThrottle::OnUIChecksPerformed, + weak_ptr_factory_.GetWeakPtr()); + DCHECK(request_->method() == "POST" || request_->method() == "GET"); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&CheckWillStartRequestOnUIThread, callback, render_process_id, + render_frame_id, request_->method() == "POST", + Referrer::SanitizeForRequest( + request_->url(), Referrer(GURL(request_->referrer()), + info->GetReferrerPolicy())), + info->HasUserGesture(), info->GetPageTransition(), + is_external_protocol)); + *defer = true; +} + +void NavigationResourceThrottle::WillRedirectRequest( + const net::RedirectInfo& redirect_info, + bool* defer) { + const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_); + if (!info) + return; + + int render_process_id, render_frame_id; + if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_id)) + return; + + bool new_is_external_protocol = + !info->GetContext()->GetRequestContext()->job_factory()->IsHandledURL( + request_->url()); + DCHECK(redirect_info.new_method == "POST" || + redirect_info.new_method == "GET"); + UIChecksPerformedCallback callback = + base::Bind(&NavigationResourceThrottle::OnUIChecksPerformed, + weak_ptr_factory_.GetWeakPtr()); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&CheckWillRedirectRequestOnUIThread, callback, + render_process_id, render_frame_id, redirect_info.new_url, + redirect_info.new_method == "POST", + GURL(redirect_info.new_referrer), new_is_external_protocol)); + *defer = true; +} + +const char* NavigationResourceThrottle::GetNameForLogging() const { + return "NavigationResourceThrottle"; +} + +void NavigationResourceThrottle::OnUIChecksPerformed( + NavigationThrottle::ThrottleCheckResult result) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (result == NavigationThrottle::CANCEL_AND_IGNORE) { + controller()->CancelAndIgnore(); + } else { + controller()->Resume(); + } +} + +} // namespace content diff --git a/content/browser/loader/navigation_resource_throttle.h b/content/browser/loader/navigation_resource_throttle.h new file mode 100644 index 0000000..e46b59d --- /dev/null +++ b/content/browser/loader/navigation_resource_throttle.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef CONTENT_BROWSER_LOADER_NAVIGATION_RESOURCE_THROTTLE_H_ +#define CONTENT_BROWSER_LOADER_NAVIGATION_RESOURCE_THROTTLE_H_ + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/navigation_throttle.h" +#include "content/public/browser/resource_throttle.h" + +namespace net { +class URLRequest; +} + +namespace content { + +// This ResourceThrottle is used to convey throttling information to the UI +// thread during navigations. The UI thread can then use its NavigationThrottle +// mechanism to interact with the navigation. +class NavigationResourceThrottle : public ResourceThrottle { + public: + NavigationResourceThrottle(net::URLRequest* request); + ~NavigationResourceThrottle() override; + + // ResourceThrottle overrides: + void WillStartRequest(bool* defer) override; + void WillRedirectRequest(const net::RedirectInfo& redirect_info, + bool* defer) override; + const char* GetNameForLogging() const override; + + private: + void OnUIChecksPerformed(NavigationThrottle::ThrottleCheckResult result); + + net::URLRequest* request_; + base::WeakPtrFactory<NavigationResourceThrottle> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(NavigationResourceThrottle); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_LOADER_NAVIGATION_RESOURCE_THROTTLE_H_ diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index c5c6796..81cbf8a 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc @@ -41,6 +41,7 @@ #include "content/browser/loader/detachable_resource_handler.h" #include "content/browser/loader/mime_type_resource_handler.h" #include "content/browser/loader/navigation_resource_handler.h" +#include "content/browser/loader/navigation_resource_throttle.h" #include "content/browser/loader/navigation_url_loader_impl_core.h" #include "content/browser/loader/power_save_block_resource_throttle.h" #include "content/browser/loader/redirect_to_file_resource_handler.h" @@ -1481,6 +1482,16 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers( plugin_service, request)); ScopedVector<ResourceThrottle> throttles; + + // Add a NavigationResourceThrottle for navigations. + // PlzNavigate: the throttle is unnecessary as communication with the UI + // thread is handled by the NavigationURLloader. + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBrowserSideNavigation) && + IsResourceTypeFrame(resource_type)) { + throttles.push_back(new NavigationResourceThrottle(request)); + } + if (delegate_) { delegate_->RequestBeginning(request, resource_context, diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 182628e..f562da9 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -182,7 +182,10 @@ 'public/browser/navigation_details.cc', 'public/browser/navigation_details.h', 'public/browser/navigation_entry.h', + 'public/browser/navigation_handle.cc', 'public/browser/navigation_handle.h', + 'public/browser/navigation_throttle.cc', + 'public/browser/navigation_throttle.h', 'public/browser/navigation_type.h', 'public/browser/navigator_connect_context.h', 'public/browser/navigator_connect_service_factory.h', @@ -972,6 +975,8 @@ 'browser/loader/mime_type_resource_handler.h', 'browser/loader/navigation_resource_handler.cc', 'browser/loader/navigation_resource_handler.h', + 'browser/loader/navigation_resource_throttle.cc', + 'browser/loader/navigation_resource_throttle.h', 'browser/loader/navigation_url_loader.cc', 'browser/loader/navigation_url_loader.h', 'browser/loader/navigation_url_loader_delegate.h', diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index 01b58bd..f25c84a 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc @@ -359,6 +359,12 @@ void ContentBrowserClient::OpenURL( callback.Run(nullptr); } +ScopedVector<NavigationThrottle> +ContentBrowserClient::CreateThrottlesForNavigation( + NavigationHandle* navigation_handle) { + return ScopedVector<NavigationThrottle>(); +} + #if defined(OS_WIN) const wchar_t* ContentBrowserClient::GetResourceDllName() { return nullptr; diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 2c6734c..55569fb 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -16,6 +16,7 @@ #include "base/memory/scoped_vector.h" #include "base/values.h" #include "content/public/browser/certificate_request_result_type.h" +#include "content/public/browser/navigation_throttle.h" #include "content/public/common/content_client.h" #include "content/public/common/media_stream_request.h" #include "content/public/common/resource_type.h" @@ -96,6 +97,7 @@ class DevToolsManagerDelegate; class ExternalVideoSurfaceContainer; class LocationProvider; class MediaObserver; +class NavigationHandle; class NavigatorConnectContext; class NavigatorConnectServiceFactory; class PlatformNotificationService; @@ -669,6 +671,14 @@ class CONTENT_EXPORT ContentBrowserClient { // Allows the embedder to record |metric| for a specific |url|. virtual void RecordURLMetric(const std::string& metric, const GURL& url) {} + // Allows the embedder to register one or more NavigationThrottles for the + // navigation indicated by |navigation_handle|. A NavigationThrottle is used + // to control the flow of a navigation on the UI thread. The embedder is + // guaranteed that the throttles will be executed in the order they were + // provided. + virtual ScopedVector<NavigationThrottle> CreateThrottlesForNavigation( + NavigationHandle* navigation_handle); + // Populates |mappings| with all files that need to be mapped before launching // a child process. #if defined(OS_ANDROID) diff --git a/content/public/browser/navigation_handle.cc b/content/public/browser/navigation_handle.cc new file mode 100644 index 0000000..f5eaa11 --- /dev/null +++ b/content/public/browser/navigation_handle.cc @@ -0,0 +1,30 @@ +// 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/public/browser/navigation_handle.h" + +#include "content/browser/frame_host/navigation_handle_impl.h" +#include "content/browser/frame_host/navigator.h" +#include "content/browser/web_contents/web_contents_impl.h" + +namespace content { + +WebContents* NavigationHandle::GetWebContents() { + // The NavigationHandleImpl cannot access the WebContentsImpl as it would be + // a layering violation, hence the cast here. + return static_cast<WebContentsImpl*>( + static_cast<NavigationHandleImpl*>(this)->delegate()); +} + +// static +scoped_ptr<NavigationHandle> NavigationHandle::CreateNavigationHandleForTesting( + const GURL& url, + bool is_main_frame, + WebContents* web_contents) { + scoped_ptr<NavigationHandleImpl> handle_impl = NavigationHandleImpl::Create( + url, is_main_frame, static_cast<WebContentsImpl*>(web_contents)); + return scoped_ptr<NavigationHandle>(handle_impl.Pass()); +} + +} // namespace content diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h index c60baba..033aca2 100644 --- a/content/public/browser/navigation_handle.h +++ b/content/public/browser/navigation_handle.h @@ -6,27 +6,70 @@ #define CONTENT_PUBLIC_BROWSER_NAVIGATION_HANDLE_H_ #include "content/common/content_export.h" +#include "content/public/browser/navigation_throttle.h" +#include "content/public/common/referrer.h" #include "net/base/net_errors.h" +#include "ui/base/page_transition_types.h" class GURL; namespace content { +class NavigationThrottle; +class WebContents; // A NavigationHandle tracks information related to a single navigation. class CONTENT_EXPORT NavigationHandle { public: virtual ~NavigationHandle() {} + // Parameters available at navigation start time ----------------------------- + // + // These parameters are always available during the navigation. Note that + // some may change during navigation (e.g. due to server redirects). + // The URL the frame is navigating to. This may change during the navigation // when encountering a server redirect. - virtual const GURL& GetURL() const = 0; + virtual const GURL& GetURL() = 0; + + // Whether the navigation is taking place in the main frame or in a subframe. + // This remains constant over the navigation lifetime. + virtual bool IsInMainFrame() = 0; + + // The WebContents the navigation is taking place in. + WebContents* GetWebContents(); + + // Parameters available at network request start time ------------------------ + // + // The following parameters are only available when the network request is + // made for the navigation (or at commit time if no network request is made). + // This corresponds to NavigationThrottle::WillSendRequest. They should not + // be queried before that. + + // Whether the navigation is a POST or a GET. This may change during the + // navigation when encountering a server redirect. + virtual bool IsPost() = 0; + + // Returns a sanitized version of the referrer for this request. + virtual const Referrer& GetReferrer() = 0; + + // Whether the navigation was initiated by a user gesture. Note that this + // will return false for browser-initiated navigations. + // TODO(clamy): when PlzNavigate launches, this should return true for + // browser-initiated navigations. + virtual bool HasUserGesture() = 0; + + // Returns the page transition type. + virtual ui::PageTransition GetPageTransition() = 0; + + // Whether the target URL cannot be handled by the browser's internal protocol + // handlers. + virtual bool IsExternalProtocol() = 0; + + // Navigation control flow -------------------------------------------------- // The net error code if an error happened prior to commit. Otherwise it will // be net::OK. - virtual net::Error GetNetErrorCode() const = 0; - - // Whether the navigation is taking place in the main frame or in a subframe. - virtual bool IsInMainFrame() const = 0; + virtual net::Error GetNetErrorCode() = 0; // Whether the navigation happened in the same page. This is only known // after the navigation has committed. It is an error to call this method @@ -34,10 +77,44 @@ class CONTENT_EXPORT NavigationHandle { virtual bool IsSamePage() = 0; // Whether the navigation has successfully committed a document. - virtual bool HasCommittedDocument() const = 0; + virtual bool HasCommittedDocument() = 0; // Whether an error page has committed for the navigation. - virtual bool HasCommittedErrorPage() const = 0; + virtual bool HasCommittedErrorPage() = 0; + + // Testing methods ---------------------------------------------------------- + // + // The following methods should be used exclusively for writing unit tests. + + static scoped_ptr<NavigationHandle> CreateNavigationHandleForTesting( + const GURL& url, + bool is_main_frame, + WebContents* web_contents); + + // Registers a NavigationThrottle for tests. The throttle can + // modify the request, pause the request or cancel the request. This will + // take ownership of the NavigationThrottle. + // Note: in non-test cases, NavigationThrottles should not be added directly + // but returned by the implementation of + // ContentBrowserClient::CreateThrottlesForNavigation. This ensures proper + // ordering of the throttles. + virtual void RegisterThrottleForTesting( + scoped_ptr<NavigationThrottle> navigation_throttle) = 0; + + // Simulates the network request starting. + virtual NavigationThrottle::ThrottleCheckResult + CallWillStartRequestForTesting(bool is_post, + const Referrer& sanitized_referrer, + bool has_user_gesture, + ui::PageTransition transition, + bool is_external_protocol) = 0; + + // Simulates the network request being redirected. + virtual NavigationThrottle::ThrottleCheckResult + CallWillRedirectRequestForTesting(const GURL& new_url, + bool new_method_is_post, + const GURL& new_referrer_url, + bool new_is_external_protocol) = 0; }; } // namespace content diff --git a/content/public/browser/navigation_throttle.cc b/content/public/browser/navigation_throttle.cc new file mode 100644 index 0000000..ac3b3aa --- /dev/null +++ b/content/public/browser/navigation_throttle.cc @@ -0,0 +1,23 @@ +// 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/public/browser/navigation_throttle.h" + +namespace content { + +NavigationThrottle::NavigationThrottle(NavigationHandle* navigation_handle) + : navigation_handle_(navigation_handle) {} + +NavigationThrottle::~NavigationThrottle() {} + +NavigationThrottle::ThrottleCheckResult NavigationThrottle::WillStartRequest() { + return NavigationThrottle::PROCEED; +} + +NavigationThrottle::ThrottleCheckResult +NavigationThrottle::WillRedirectRequest() { + return NavigationThrottle::PROCEED; +} + +} // namespace content diff --git a/content/public/browser/navigation_throttle.h b/content/public/browser/navigation_throttle.h new file mode 100644 index 0000000..ae5883b --- /dev/null +++ b/content/public/browser/navigation_throttle.h @@ -0,0 +1,43 @@ +// 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. + +#ifndef CONTENT_PUBLIC_BROWSER_NAVIGATION_THROTTLE_H_ +#define CONTENT_PUBLIC_BROWSER_NAVIGATION_THROTTLE_H_ + +#include "content/common/content_export.h" + +namespace content { +class NavigationHandle; + +// A NavigationThrottle tracks and allows interaction with a navigation on the +// UI thread. +class CONTENT_EXPORT NavigationThrottle { + public: + // This is returned to the NavigationHandle to allow the navigation to + // proceed, or to cancel it. + enum ThrottleCheckResult { + PROCEED, + CANCEL_AND_IGNORE, + }; + + NavigationThrottle(NavigationHandle* navigation_handle); + virtual ~NavigationThrottle(); + + // Called when a network request is about to be made for this navigation. + virtual ThrottleCheckResult WillStartRequest(); + + // Called when a server redirect is received by the navigation. + virtual ThrottleCheckResult WillRedirectRequest(); + + // The NavigationHandle that is tracking the information related to this + // navigation. + NavigationHandle* navigation_handle() const { return navigation_handle_; } + + private: + NavigationHandle* navigation_handle_; +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_BROWSER_NAVIGATION_THROTTLE_H_ diff --git a/content/test/web_contents_observer_sanity_checker.cc b/content/test/web_contents_observer_sanity_checker.cc index 82bcb60..18d38ae 100644 --- a/content/test/web_contents_observer_sanity_checker.cc +++ b/content/test/web_contents_observer_sanity_checker.cc @@ -129,6 +129,7 @@ void WebContentsObserverSanityChecker::DidStartNavigation( CHECK(navigation_handle->GetNetErrorCode() == net::OK); CHECK(!navigation_handle->HasCommittedDocument()); CHECK(!navigation_handle->HasCommittedErrorPage()); + CHECK_EQ(navigation_handle->GetWebContents(), web_contents()); ongoing_navigations_.insert(navigation_handle); } @@ -141,6 +142,7 @@ void WebContentsObserverSanityChecker::DidRedirectNavigation( CHECK(navigation_handle->GetNetErrorCode() == net::OK); CHECK(!navigation_handle->HasCommittedDocument()); CHECK(!navigation_handle->HasCommittedErrorPage()); + CHECK_EQ(navigation_handle->GetWebContents(), web_contents()); } void WebContentsObserverSanityChecker::ReadyToCommitNavigation( @@ -163,6 +165,7 @@ void WebContentsObserverSanityChecker::DidCommitNavigation( navigation_handle->GetNetErrorCode() == net::OK); CHECK_IMPLIES(navigation_handle->HasCommittedErrorPage(), navigation_handle->GetNetErrorCode() != net::OK); + CHECK_EQ(navigation_handle->GetWebContents(), web_contents()); ongoing_committed_navigations_.insert(navigation_handle); } @@ -178,6 +181,7 @@ void WebContentsObserverSanityChecker::DidFinishNavigation( navigation_handle->GetNetErrorCode() == net::OK); CHECK_IMPLIES(navigation_handle->HasCommittedErrorPage(), navigation_handle->GetNetErrorCode() != net::OK); + CHECK_EQ(navigation_handle->GetWebContents(), web_contents()); if (NavigationIsOngoingAndCommitted(navigation_handle)) ongoing_committed_navigations_.erase(navigation_handle); |