diff options
Diffstat (limited to 'chromium/components/navigation_interception')
15 files changed, 1107 insertions, 0 deletions
diff --git a/chromium/components/navigation_interception/DEPS b/chromium/components/navigation_interception/DEPS new file mode 100644 index 00000000000..cee4d3e1b1c --- /dev/null +++ b/chromium/components/navigation_interception/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + "+jni", + "+net", + + "+content/public/browser", + "+content/public/common", + "+content/public/test", +] diff --git a/chromium/components/navigation_interception/OWNERS b/chromium/components/navigation_interception/OWNERS new file mode 100644 index 00000000000..82ab3736cc6 --- /dev/null +++ b/chromium/components/navigation_interception/OWNERS @@ -0,0 +1,2 @@ +joth@chromium.org +mkosiba@chromium.org diff --git a/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java b/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java new file mode 100644 index 00000000000..f2144675e1b --- /dev/null +++ b/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java @@ -0,0 +1,21 @@ +// 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. + +package org.chromium.components.navigation_interception; + +import org.chromium.base.CalledByNative; + +public interface InterceptNavigationDelegate { + + /** + * This method is called for every top-level navigation within the associated WebContents. + * The method allows the embedder to ignore navigations. This is used on Android to 'convert' + * certain navigations to Intents to 3rd party applications. + * + * @param navigationParams parameters describing the navigation. + * @return true if the navigation should be ignored. + */ + @CalledByNative + boolean shouldIgnoreNavigation(NavigationParams navigationParams); +} diff --git a/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java b/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java new file mode 100644 index 00000000000..cdfd88313f7 --- /dev/null +++ b/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java @@ -0,0 +1,36 @@ +// Copyright (c) 2013 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. + +package org.chromium.components.navigation_interception; + +import org.chromium.base.CalledByNative; + +public class NavigationParams { + // Target url of the navigation. + public final String url; + // True if the the navigation method is "POST". + public final boolean isPost; + // True if the navigation was initiated by the user. + public final boolean hasUserGesture; + // Page transition type (e.g. link / typed). + public final int pageTransitionType; + // Is the navigation a redirect (in which case url is the "target" address). + public final boolean isRedirect; + + public NavigationParams(String url, boolean isPost, boolean hasUserGesture, + int pageTransitionType, boolean isRedirect) { + this.url = url; + this.isPost = isPost; + this.hasUserGesture = hasUserGesture; + this.pageTransitionType = pageTransitionType; + this.isRedirect = isRedirect; + } + + @CalledByNative + public static NavigationParams create(String url, boolean isPost, boolean hasUserGesture, + int pageTransitionType, boolean isRedirect) { + return new NavigationParams(url, isPost, hasUserGesture, pageTransitionType, + isRedirect); + } +} diff --git a/chromium/components/navigation_interception/component_jni_registrar.cc b/chromium/components/navigation_interception/component_jni_registrar.cc new file mode 100644 index 00000000000..9663529d7cc --- /dev/null +++ b/chromium/components/navigation_interception/component_jni_registrar.cc @@ -0,0 +1,24 @@ +// 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/component_jni_registrar.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "components/navigation_interception/intercept_navigation_delegate.h" +#include "components/navigation_interception/navigation_params_android.h" + +namespace navigation_interception { + +static base::android::RegistrationMethod kComponentRegisteredMethods[] = { + { "InterceptNavigationDelegate", RegisterInterceptNavigationDelegate }, + { "NavigationParams", RegisterNavigationParams }, +}; + +bool RegisterNavigationInterceptionJni(JNIEnv* env) { + return RegisterNativeMethods( + env, kComponentRegisteredMethods, arraysize(kComponentRegisteredMethods)); +} + +} // namespace navigation_interception diff --git a/chromium/components/navigation_interception/component_jni_registrar.h b/chromium/components/navigation_interception/component_jni_registrar.h new file mode 100644 index 00000000000..c79f3778e9f --- /dev/null +++ b/chromium/components/navigation_interception/component_jni_registrar.h @@ -0,0 +1,18 @@ +// 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_COMPONENT_JNI_REGISTRAR_H_ +#define COMPONENTS_NAVIGATION_INTERCEPTION_COMPONENT_JNI_REGISTRAR_H_ + +#include <jni.h> + +namespace navigation_interception { + +// Register all JNI bindings necessary for the navigation_interception +// component. +bool RegisterNavigationInterceptionJni(JNIEnv* env); + +} // namespace navigation_interception + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_COMPONENT_JNI_REGISTRAR_H_ diff --git a/chromium/components/navigation_interception/intercept_navigation_delegate.cc b/chromium/components/navigation_interception/intercept_navigation_delegate.cc new file mode 100644 index 00000000000..1cbe04c8e7e --- /dev/null +++ b/chromium/components/navigation_interception/intercept_navigation_delegate.cc @@ -0,0 +1,107 @@ +// 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_delegate.h" + +#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/navigation_params_android.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "jni/InterceptNavigationDelegate_jni.h" +#include "net/url_request/url_request.h" +#include "url/gurl.h" + +using base::android::ConvertUTF8ToJavaString; +using base::android::ScopedJavaLocalRef; +using content::BrowserThread; +using content::PageTransition; +using content::RenderViewHost; +using content::WebContents; + +namespace navigation_interception { + +namespace { + +const void* kInterceptNavigationDelegateUserDataKey = + &kInterceptNavigationDelegateUserDataKey; + +bool CheckIfShouldIgnoreNavigationOnUIThread(RenderViewHost* source, + const NavigationParams& params) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(source); + + WebContents* web_contents = WebContents::FromRenderViewHost(source); + if (!web_contents) + return false; + InterceptNavigationDelegate* intercept_navigation_delegate = + InterceptNavigationDelegate::Get(web_contents); + if (!intercept_navigation_delegate) + return false; + + return intercept_navigation_delegate->ShouldIgnoreNavigation(params); +} + +} // namespace + +// static +void InterceptNavigationDelegate::Associate( + WebContents* web_contents, + scoped_ptr<InterceptNavigationDelegate> delegate) { + web_contents->SetUserData(kInterceptNavigationDelegateUserDataKey, + delegate.release()); +} + +// static +InterceptNavigationDelegate* InterceptNavigationDelegate::Get( + WebContents* web_contents) { + return reinterpret_cast<InterceptNavigationDelegate*>( + web_contents->GetUserData(kInterceptNavigationDelegateUserDataKey)); +} + +// static +content::ResourceThrottle* InterceptNavigationDelegate::CreateThrottleFor( + net::URLRequest* request) { + return new InterceptNavigationResourceThrottle( + request, base::Bind(&CheckIfShouldIgnoreNavigationOnUIThread)); +} + +InterceptNavigationDelegate::InterceptNavigationDelegate( + JNIEnv* env, jobject jdelegate) + : weak_jdelegate_(env, jdelegate) { +} + +InterceptNavigationDelegate::~InterceptNavigationDelegate() { +} + +bool InterceptNavigationDelegate::ShouldIgnoreNavigation( + const NavigationParams& navigation_params) { + if (!navigation_params.url().is_valid()) + return false; + + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env); + + if (jdelegate.is_null()) + return false; + + ScopedJavaLocalRef<jobject> jobject_params = + CreateJavaNavigationParams(env, navigation_params); + + return Java_InterceptNavigationDelegate_shouldIgnoreNavigation( + env, + jdelegate.obj(), + jobject_params.obj()); +} + +// Register native methods. + +bool RegisterInterceptNavigationDelegate(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace navigation_interception diff --git a/chromium/components/navigation_interception/intercept_navigation_delegate.h b/chromium/components/navigation_interception/intercept_navigation_delegate.h new file mode 100644 index 00000000000..c70beef978a --- /dev/null +++ b/chromium/components/navigation_interception/intercept_navigation_delegate.h @@ -0,0 +1,69 @@ +// 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_DELEGATE_H_ +#define COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_DELEGATE_H_ + +#include "base/android/jni_helper.h" +#include "base/memory/scoped_ptr.h" +#include "base/supports_user_data.h" +#include "content/public/common/page_transition_types.h" + +class GURL; + +namespace content { +class ResourceThrottle; +class WebContents; +} + +namespace net { +class URLRequest; +} + +namespace navigation_interception { + +class NavigationParams; + +// Native side of the InterceptNavigationDelegate Java interface. +// This is used to create a InterceptNavigationResourceThrottle that calls the +// Java interface method to determine whether a navigation should be ignored or +// not. +// To us this class: +// 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. +class InterceptNavigationDelegate : public base::SupportsUserData::Data { + public: + InterceptNavigationDelegate(JNIEnv* env, jobject jdelegate); + virtual ~InterceptNavigationDelegate(); + + // Associates the InterceptNavigationDelegate with a WebContents using the + // SupportsUserData mechanism. + // As implied by the use of scoped_ptr, the WebContents will assume ownership + // of |delegate|. + static void Associate(content::WebContents* web_contents, + scoped_ptr<InterceptNavigationDelegate> delegate); + // Gets the InterceptNavigationDelegate associated with the WebContents, + // 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); + + virtual bool ShouldIgnoreNavigation( + const NavigationParams& navigation_params); + private: + JavaObjectWeakGlobalRef weak_jdelegate_; +}; + +bool RegisterInterceptNavigationDelegate(JNIEnv* env); + +} // namespace navigation_interception + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_DELEGATE_H_ diff --git a/chromium/components/navigation_interception/intercept_navigation_resource_throttle.cc b/chromium/components/navigation_interception/intercept_navigation_resource_throttle.cc new file mode 100644 index 00000000000..73513ef4752 --- /dev/null +++ b/chromium/components/navigation_interception/intercept_navigation_resource_throttle.cc @@ -0,0 +1,140 @@ +// 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_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/resource_controller.h" +#include "content/public/browser/resource_request_info.h" +#include "content/public/common/page_transition_types.h" +#include "content/public/common/referrer.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request.h" + +using content::BrowserThread; +using content::ChildProcessSecurityPolicy; +using content::PageTransition; +using content::Referrer; +using content::RenderViewHost; +using content::ResourceRequestInfo; + +namespace navigation_interception { + +namespace { + +void CheckIfShouldIgnoreNavigationOnUIThread( + int render_process_id, + int render_view_id, + const NavigationParams& navigation_params, + InterceptNavigationResourceThrottle::CheckOnUIThreadCallback + should_ignore_callback, + base::Callback<void(bool)> callback) { + + bool should_ignore_navigation = false; + RenderViewHost* rvh = + RenderViewHost::FromID(render_process_id, render_view_id); + + if (rvh) { + NavigationParams validated_params(navigation_params); + RenderViewHost::FilterURL( + rvh->GetProcess(), false, &validated_params.url()); + + should_ignore_navigation = should_ignore_callback.Run(rvh, + 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(BrowserThread::CurrentlyOn(BrowserThread::IO)); +} + +void InterceptNavigationResourceThrottle::WillStartRequest(bool* defer) { + *defer = + CheckIfShouldIgnoreNavigation(request_->url(), request_->method(), false); +} + +void InterceptNavigationResourceThrottle::WillRedirectRequest( + const GURL& new_url, + bool* defer) { + *defer = + CheckIfShouldIgnoreNavigation(new_url, GetMethodAfterRedirect(), true); +} + +std::string InterceptNavigationResourceThrottle::GetMethodAfterRedirect() { + net::HttpResponseHeaders* headers = request_->response_headers(); + if (!headers) + return request_->method(); + return net::URLRequest::ComputeMethodForRedirect( + request_->method(), headers->response_code()); +} + +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_view_id; + if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id)) + return false; + + NavigationParams navigation_params(url, + Referrer(GURL(request_->referrer()), + info->GetReferrerPolicy()), + info->HasUserGesture(), + method == "POST", + info->GetPageTransition(), + is_redirect); + + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind( + &CheckIfShouldIgnoreNavigationOnUIThread, + render_process_id, + render_view_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(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (should_ignore_navigation) { + controller()->CancelAndIgnore(); + } else { + controller()->Resume(); + } +} + +} // namespace navigation_interception diff --git a/chromium/components/navigation_interception/intercept_navigation_resource_throttle.h b/chromium/components/navigation_interception/intercept_navigation_resource_throttle.h new file mode 100644 index 00000000000..79ed57e44c1 --- /dev/null +++ b/chromium/components/navigation_interception/intercept_navigation_resource_throttle.h @@ -0,0 +1,62 @@ +// 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 RenderViewHost; +} + +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::RenderViewHost* /* source */, + const NavigationParams& /* navigation_params */)> + CheckOnUIThreadCallback; + + InterceptNavigationResourceThrottle( + net::URLRequest* request, + CheckOnUIThreadCallback should_ignore_callback); + virtual ~InterceptNavigationResourceThrottle(); + + // content::ResourceThrottle implementation: + virtual void WillStartRequest(bool* defer) OVERRIDE; + virtual void WillRedirectRequest(const GURL& new_url, bool* defer) OVERRIDE; + + private: + std::string GetMethodAfterRedirect(); + 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/chromium/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc b/chromium/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc new file mode 100644 index 00000000000..e1bd566b54e --- /dev/null +++ b/chromium/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc @@ -0,0 +1,475 @@ +// 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/memory/scoped_vector.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_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/common/page_transition_types.h" +#include "content/public/test/mock_resource_context.h" +#include "content/public/test/test_renderer_host.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_response_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 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::RenderViewHost* 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: + virtual void Cancel() OVERRIDE { + NOTREACHED(); + } + virtual void CancelAndIgnore() OVERRIDE { + status_ = CANCELLED; + } + virtual void CancelWithError(int error_code) OVERRIDE { + NOTREACHED(); + } + virtual 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_view_id, + const std::string& request_method, + RedirectMode redirect_mode, + MockInterceptCallbackReceiver* callback_receiver) + : resource_context_(&test_url_request_context_), + request_(url, NULL, resource_context_.GetRequestContext()) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + if (render_process_id != MSG_ROUTING_NONE && + render_view_id != MSG_ROUTING_NONE) { + content::ResourceRequestInfo::AllocateForTesting( + &request_, + ResourceType::MAIN_FRAME, + &resource_context_, + render_process_id, + render_view_id); + } + throttle_.reset(new InterceptNavigationResourceThrottle( + &request_, + 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(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + throttle_->WillStartRequest(defer); + } + + void ThrottleWillRedirectRequest(const GURL& new_url, bool* defer) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + throttle_->WillRedirectRequest(new_url, 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_; + 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) { + } + + virtual void SetUp() OVERRIDE { + RenderViewHostTestHarness::SetUp(); + } + + virtual void TearDown() OVERRIDE { + if (web_contents()) + web_contents()->SetDelegate(NULL); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(&base::DeletePointer<TestIOThreadState>, 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_view_id, + bool* defer) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + TestIOThreadState* io_thread_state = + new TestIOThreadState(url, render_process_id, render_view_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 + io_thread_state->ThrottleWillRedirectRequest(url, 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(rvh(), 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()->GetRenderViewHost()->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); + EXPECT_TRUE(io_thread_state_); + EXPECT_TRUE(io_thread_state_->request_resumed()); +} + +TEST_F(InterceptNavigationResourceThrottleTest, + RequestDeferredAndCancelledIfNavigationIgnored) { + bool defer = false; + SetUpWebContentsDelegateAndDrainRunLoop(IgnoreNavigation, &defer); + + EXPECT_TRUE(defer); + EXPECT_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()->GetRenderViewHost()->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); + EXPECT_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()->GetRenderViewHost()->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()->GetRenderViewHost()->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()->GetRenderViewHost()->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()->GetRenderViewHost()->GetRoutingID(), + base::Unretained(&defer))); + + // Wait for the request to finish processing. + base::RunLoop().RunUntilIdle(); +} + +} // namespace navigation_interception diff --git a/chromium/components/navigation_interception/navigation_params.cc b/chromium/components/navigation_interception/navigation_params.cc new file mode 100644 index 00000000000..2bb0e94b58d --- /dev/null +++ b/chromium/components/navigation_interception/navigation_params.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2013 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/navigation_params.h" + +namespace navigation_interception { + +NavigationParams::NavigationParams(const NavigationParams& other) { + Assign(other); +} + +NavigationParams::NavigationParams( + const GURL& url, + const content::Referrer& referrer, + bool has_user_gesture, + bool is_post, + content::PageTransition transition_type, + bool is_redirect) + : url_(url), + referrer_(referrer), + has_user_gesture_(has_user_gesture), + is_post_(is_post), + transition_type_(transition_type), + is_redirect_(is_redirect) { +} + +void NavigationParams::operator=(const NavigationParams& rhs) { + Assign(rhs); +} + +void NavigationParams::Assign(const NavigationParams& other) { + url_ = other.url(); + referrer_ = other.referrer(); + has_user_gesture_ = other.has_user_gesture(); + is_post_ = other.is_post(); + transition_type_ = other.transition_type(); + is_redirect_ = other.is_redirect(); +} + +} // namespace navigation_interception + diff --git a/chromium/components/navigation_interception/navigation_params.h b/chromium/components/navigation_interception/navigation_params.h new file mode 100644 index 00000000000..0144a5af023 --- /dev/null +++ b/chromium/components/navigation_interception/navigation_params.h @@ -0,0 +1,47 @@ +// Copyright (c) 2013 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_NAVIGATION_PARAMS_H_ +#define COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_ + +#include "content/public/common/page_transition_types.h" +#include "content/public/common/page_transition_types.h" +#include "content/public/common/referrer.h" +#include "url/gurl.h" + +namespace navigation_interception { + +class NavigationParams { + public: + NavigationParams(const GURL& url, + const content::Referrer& referrer, + bool has_user_gesture, + bool is_post, + content::PageTransition page_transition_type, + bool is_redirect); + NavigationParams(const NavigationParams& other); + void operator=(const NavigationParams& rhs); + + const GURL& url() const { return url_; } + GURL& url() { return url_; } + const content::Referrer& referrer() const { return referrer_; } + bool has_user_gesture() const { return has_user_gesture_; } + bool is_post() const { return is_post_; } + content::PageTransition transition_type() const { return transition_type_; } + bool is_redirect() const { return is_redirect_; } + + private: + void Assign(const NavigationParams& other); + + GURL url_; + content::Referrer referrer_; + bool has_user_gesture_; + bool is_post_; + content::PageTransition transition_type_; + bool is_redirect_; +}; + +} // namespace navigation_interception + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_ diff --git a/chromium/components/navigation_interception/navigation_params_android.cc b/chromium/components/navigation_interception/navigation_params_android.cc new file mode 100644 index 00000000000..769450fa9d2 --- /dev/null +++ b/chromium/components/navigation_interception/navigation_params_android.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2013 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/navigation_params_android.h" + +#include "base/android/jni_string.h" +#include "jni/NavigationParams_jni.h" + +using base::android::ConvertUTF8ToJavaString; + +namespace navigation_interception { + +base::android::ScopedJavaLocalRef<jobject> CreateJavaNavigationParams( + JNIEnv* env, + const NavigationParams& params) { + ScopedJavaLocalRef<jstring> jstring_url = + ConvertUTF8ToJavaString(env, params.url().spec()); + + return Java_NavigationParams_create(env, + jstring_url.obj(), + params.is_post(), + params.has_user_gesture(), + params.transition_type(), + params.is_redirect()); +} + +// Register native methods. + +bool RegisterNavigationParams(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace navigation_interception diff --git a/chromium/components/navigation_interception/navigation_params_android.h b/chromium/components/navigation_interception/navigation_params_android.h new file mode 100644 index 00000000000..004d4927384 --- /dev/null +++ b/chromium/components/navigation_interception/navigation_params_android.h @@ -0,0 +1,22 @@ +// Copyright (c) 2013 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_NAVIGATION_PARAMS_ANDROID_H_ +#define COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_ANDROID_H_ + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" +#include "components/navigation_interception/navigation_params.h" + +namespace navigation_interception { + +base::android::ScopedJavaLocalRef<jobject> CreateJavaNavigationParams( + JNIEnv* env, + const NavigationParams& params); + +bool RegisterNavigationParams(JNIEnv* env); + +} // namespace navigation_interception + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_ANDROID_H_ |
