diff options
author | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-23 03:55:16 +0000 |
---|---|---|
committer | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-23 03:55:16 +0000 |
commit | 62885abfdaced9bad3f119e5f4f15b81f34c02c6 (patch) | |
tree | 97eba6bb62094c692561f9355d037a392e72e54f /components/navigation_interception | |
parent | 62c607121a2c0b5bf463cd6da0fd568140523e30 (diff) | |
download | chromium_src-62885abfdaced9bad3f119e5f4f15b81f34c02c6.zip chromium_src-62885abfdaced9bad3f119e5f4f15b81f34c02c6.tar.gz chromium_src-62885abfdaced9bad3f119e5f4f15b81f34c02c6.tar.bz2 |
Move content/components/navigation_interception to src/components
BUG=169312
Review URL: https://chromiumcodereview.appspot.com/11830043
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@178238 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/navigation_interception')
10 files changed, 875 insertions, 0 deletions
diff --git a/components/navigation_interception/DEPS b/components/navigation_interception/DEPS new file mode 100644 index 0000000..cee4d3e --- /dev/null +++ b/components/navigation_interception/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + "+jni", + "+net", + + "+content/public/browser", + "+content/public/common", + "+content/public/test", +] diff --git a/components/navigation_interception/OWNERS b/components/navigation_interception/OWNERS new file mode 100644 index 0000000..d7b0401 --- /dev/null +++ b/components/navigation_interception/OWNERS @@ -0,0 +1,3 @@ +jknotten@chromium.org +joth@chromium.org +mkosiba@chromium.org diff --git a/components/navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java b/components/navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java new file mode 100644 index 0000000..4f3938e --- /dev/null +++ b/components/navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java @@ -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. + +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 url the target url of the navigation. + * @param isPost true if the the navigation method is "POST". + * @param isUserGesture true if the navigation was initiated by the user. + * @param pageTransition is the page transition type (e.g. link / typed). + * @return true if the navigation should be ignored. + */ + @CalledByNative + boolean shouldIgnoreNavigation(String url, boolean isPost, boolean isUserGesture, + int pageTransition); +} diff --git a/components/navigation_interception/component_jni_registrar.cc b/components/navigation_interception/component_jni_registrar.cc new file mode 100644 index 0000000..9d2c30e --- /dev/null +++ b/components/navigation_interception/component_jni_registrar.cc @@ -0,0 +1,22 @@ +// 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" + +namespace components { + +static base::android::RegistrationMethod kComponentRegisteredMethods[] = { + { "InterceptNavigationDelegate", RegisterInterceptNavigationDelegate }, +}; + +bool RegisterNavigationInterceptionJni(JNIEnv* env) { + return RegisterNativeMethods(env, + kComponentRegisteredMethods, arraysize(kComponentRegisteredMethods)); +} + +} // namespace components diff --git a/components/navigation_interception/component_jni_registrar.h b/components/navigation_interception/component_jni_registrar.h new file mode 100644 index 0000000..ada59b2 --- /dev/null +++ b/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 components { + +// Register all JNI bindings necessary for the navigation_interception +// component. +bool RegisterNavigationInterceptionJni(JNIEnv* env); + +} // namespace components + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_COMPONENT_JNI_REGISTRAR_H_ diff --git a/components/navigation_interception/intercept_navigation_delegate.cc b/components/navigation_interception/intercept_navigation_delegate.cc new file mode 100644 index 0000000..a0cbc86 --- /dev/null +++ b/components/navigation_interception/intercept_navigation_delegate.cc @@ -0,0 +1,116 @@ +// 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 "content/public/browser/browser_thread.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "googleurl/src/gurl.h" +#include "jni/InterceptNavigationDelegate_jni.h" +#include "net/url_request/url_request.h" + +using base::android::ConvertUTF8ToJavaString; +using base::android::ScopedJavaLocalRef; +using content::BrowserThread; +using content::PageTransition; +using content::RenderViewHost; +using content::WebContents; + +namespace components { + +namespace { + +const void* kInterceptNavigationDelegateUserDataKey = + &kInterceptNavigationDelegateUserDataKey; + +bool CheckIfShouldIgnoreNavigationOnUIThread(RenderViewHost* source, + const GURL& url, + const content::Referrer& referrer, + bool is_post, + bool has_user_gesture, + PageTransition transition_type) { + 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( + url, is_post, has_user_gesture, transition_type); +} + +} // 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 GURL& url, + bool is_post, + bool has_user_gesture, + PageTransition transition_type) { + if (!url.is_valid()) + return false; + + JNIEnv* env = base::android::AttachCurrentThread(); + ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env); + + if (jdelegate.is_null()) + return false; + + ScopedJavaLocalRef<jstring> jstring_url = + ConvertUTF8ToJavaString(env, url.spec()); + return Java_InterceptNavigationDelegate_shouldIgnoreNavigation( + env, + jdelegate.obj(), + jstring_url.obj(), + is_post, + has_user_gesture, + transition_type); +} + +// Register native methods. + +bool RegisterInterceptNavigationDelegate(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace components diff --git a/components/navigation_interception/intercept_navigation_delegate.h b/components/navigation_interception/intercept_navigation_delegate.h new file mode 100644 index 0000000..66a2518 --- /dev/null +++ b/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 components { + +// 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 GURL& url, + bool is_post, + bool has_user_gesture, + content::PageTransition transition_type); + private: + JavaObjectWeakGlobalRef weak_jdelegate_; +}; + +bool RegisterInterceptNavigationDelegate(JNIEnv* env); + +} // namespace components + +#endif // COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_DELEGATE_H_ diff --git a/components/navigation_interception/intercept_navigation_resource_throttle.cc b/components/navigation_interception/intercept_navigation_resource_throttle.cc new file mode 100644 index 0000000..5f7c090 --- /dev/null +++ b/components/navigation_interception/intercept_navigation_resource_throttle.cc @@ -0,0 +1,138 @@ +// 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 "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/url_request/url_request.h" + +using content::BrowserThread; +using content::ChildProcessSecurityPolicy; +using content::PageTransition; +using content::Referrer; +using content::RenderViewHost; +using content::ResourceRequestInfo; + +namespace components { + +namespace { + +struct ShouldIgnoreCallbackParams { + int render_process_id; + int render_view_id; + GURL url; + Referrer referrer; + bool has_user_gesture; + bool is_post; + PageTransition transition_type; +}; + +void CheckIfShouldIgnoreNavigationOnUIThread( + const ShouldIgnoreCallbackParams& params, + InterceptNavigationResourceThrottle::CheckOnUIThreadCallback + should_ignore_callback, + base::Callback<void(bool)> callback) { + + bool should_ignore_navigation = false; + RenderViewHost* rvh = + RenderViewHost::FromID(params.render_process_id, params.render_view_id); + + if (rvh) { + GURL validated_url(params.url); + RenderViewHost::FilterURL(rvh->GetProcess(), false, &validated_url); + + should_ignore_navigation = should_ignore_callback.Run( + rvh, + validated_url, + params.referrer, + params.is_post, + params.has_user_gesture, + params.transition_type); + } + + 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_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { +} + +InterceptNavigationResourceThrottle::~InterceptNavigationResourceThrottle() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); +} + +void InterceptNavigationResourceThrottle::WillStartRequest(bool* defer) { + *defer = CheckIfShouldIgnoreNavigation(request_->url()); +} + +void InterceptNavigationResourceThrottle::WillRedirectRequest( + const GURL& new_url, + bool* defer) { + *defer = CheckIfShouldIgnoreNavigation(new_url); +} + +bool InterceptNavigationResourceThrottle::CheckIfShouldIgnoreNavigation( + const GURL& url) { + 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; + + ShouldIgnoreCallbackParams params; + params.render_process_id = render_process_id; + params.render_view_id = render_view_id; + params.url = url; + params.referrer = Referrer(GURL(request_->referrer()), + info->GetReferrerPolicy()); + params.has_user_gesture = info->HasUserGesture(); + params.is_post = request_->method() == "POST"; + params.transition_type = info->GetPageTransition(); + + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind( + &CheckIfShouldIgnoreNavigationOnUIThread, + 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 components diff --git a/components/navigation_interception/intercept_navigation_resource_throttle.h b/components/navigation_interception/intercept_navigation_resource_throttle.h new file mode 100644 index 0000000..7bfe6fa --- /dev/null +++ b/components/navigation_interception/intercept_navigation_resource_throttle.h @@ -0,0 +1,63 @@ +// 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" +#include "content/public/common/page_transition_types.h" + +class GURL; + +namespace content { +class RenderViewHost; +struct Referrer; +} + +namespace net { +class URLRequest; +} + +namespace components { + +// 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 GURL& /* url */, + const content::Referrer& /*referrer*/, + bool /* is_post */, + bool /* has_user_gesture */, + content::PageTransition /* page transition type */)> + 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: + bool CheckIfShouldIgnoreNavigation(const GURL& url); + 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 components + +#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 new file mode 100644 index 0000000..8d9943c --- /dev/null +++ b/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc @@ -0,0 +1,414 @@ +// 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/synchronization/waitable_event.h" +#include "components/navigation_interception/intercept_navigation_resource_throttle.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_browser_thread.h" +#include "content/public/test/test_renderer_host.h" +#include "net/url_request/url_request.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::Return; + +namespace components { + +namespace { + +const char kTestUrl[] = "http://www.test.com/"; +const char kUnsafeTestUrl[] = "about:crash"; + +void ContinueTestCase() { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, MessageLoop::QuitClosure()); +} + +} // namespace + +// MockInterceptCallbackReceiver ---------------------------------------------- + +class MockInterceptCallbackReceiver { + public: + MOCK_METHOD6(ShouldIgnoreNavigation, + bool(content::RenderViewHost* source, + const GURL& url, + const content::Referrer& referrer, + bool is_post, + bool has_user_gesture, + content::PageTransition page_transition)); +}; + +// MockResourceController ----------------------------------------------------- +class MockResourceController : public content::ResourceController { + public: + enum Status { + UNKNOWN, + RESUMED, + CANCELLED + }; + + MockResourceController() + : status_(UNKNOWN) { + } + + Status status() const { return status_; } + + // ResourceController: + virtual void Cancel() { + NOTREACHED(); + } + virtual void CancelAndIgnore() { + status_ = CANCELLED; + ContinueTestCase(); + } + virtual void CancelWithError(int error_code) { + NOTREACHED(); + } + virtual void Resume() { + DCHECK(status_ == UNKNOWN); + status_ = RESUMED; + ContinueTestCase(); + } + + private: + Status status_; +}; + +// TestIOThreadState ---------------------------------------------------------- + +class TestIOThreadState { + public: + TestIOThreadState(const GURL& url, int render_process_id, int render_view_id, + const std::string& request_method, + MockInterceptCallbackReceiver* callback_receiver) + : request_(url, NULL, resource_context_.GetRequestContext()), + throttle_(NULL) { + 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); + } + + void ThrottleWillStartRequest(bool* defer) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + throttle_->WillStartRequest(defer); + } + + bool request_resumed() const { + return throttle_controller_.status() == + MockResourceController::RESUMED; + } + + bool request_cancelled() const { + return throttle_controller_.status() == + MockResourceController::CANCELLED; + } + + private: + 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()), + ui_thread_(content::BrowserThread::UI, &message_loop_), + io_thread_(content::BrowserThread::IO), + io_thread_state_(NULL) { + } + + virtual void SetUp() OVERRIDE { + RenderViewHostTestHarness::SetUp(); + + io_thread_.StartIOThread(); + } + + 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, + 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, mock_callback_receiver_.get()); + + SetIOThreadState(io_thread_state); + io_thread_state->ThrottleWillStartRequest(defer); + + if (!*defer) { + ContinueTestCase(); + } + } + + protected: + enum ShouldIgnoreNavigationCallbackAction { + IgnoreNavigation, + DontIgnoreNavigation + }; + + void SetUpWebContentsDelegateAndRunMessageLoop( + ShouldIgnoreNavigationCallbackAction callback_action, + bool* defer) { + + ON_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation(_, _, _, _, _, _)) + .WillByDefault(Return(callback_action == IgnoreNavigation)); + EXPECT_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation(rvh(), Eq(GURL(kTestUrl)), _, _, _, _)) + .Times(1); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind( + &InterceptNavigationResourceThrottleTest:: + RunThrottleWillStartRequestOnIOThread, + base::Unretained(this), + GURL(kTestUrl), + "GET", + web_contents()->GetRenderViewHost()->GetProcess()->GetID(), + web_contents()->GetRenderViewHost()->GetRoutingID(), + base::Unretained(defer))); + + // Wait for the request to finish processing. + message_loop_.Run(); + } + + 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_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread io_thread_; + TestIOThreadState* io_thread_state_; +}; + +TEST_F(InterceptNavigationResourceThrottleTest, + RequestDeferredAndResumedIfNavigationNotIgnored) { + bool defer = false; + SetUpWebContentsDelegateAndRunMessageLoop(DontIgnoreNavigation, &defer); + + EXPECT_TRUE(defer); + EXPECT_TRUE(io_thread_state_); + EXPECT_TRUE(io_thread_state_->request_resumed()); +} + +TEST_F(InterceptNavigationResourceThrottleTest, + RequestDeferredAndCancelledIfNavigationIgnored) { + bool defer = false; + SetUpWebContentsDelegateAndRunMessageLoop(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. + content::BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind( + &RenderViewHostTestHarness::DeleteContents, + base::Unretained(this))); + + 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", + web_contents()->GetRenderViewHost()->GetProcess()->GetID(), + web_contents()->GetRenderViewHost()->GetRoutingID(), + base::Unretained(&defer))); + + WaitForPreviouslyScheduledIoThreadWork(); + + // The WebContents will now be deleted and only after that will the UI-thread + // callback posted by the ResourceThrottle be executed. + message_loop_.Run(); + + 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", + MSG_ROUTING_NONE, + MSG_ROUTING_NONE, + base::Unretained(&defer))); + + // Wait for the request to finish processing. + message_loop_.Run(); + + EXPECT_FALSE(defer); +} + +TEST_F(InterceptNavigationResourceThrottleTest, + CallbackCalledWithFilteredUrl) { + bool defer = false; + + ON_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation(_, Ne(GURL(kUnsafeTestUrl)), _, _, _, _)) + .WillByDefault(Return(false)); + EXPECT_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation(_, Ne(GURL(kUnsafeTestUrl)), _, _, _, _)) + .Times(1); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind( + &InterceptNavigationResourceThrottleTest:: + RunThrottleWillStartRequestOnIOThread, + base::Unretained(this), + GURL(kUnsafeTestUrl), + "GET", + web_contents()->GetRenderViewHost()->GetProcess()->GetID(), + web_contents()->GetRenderViewHost()->GetRoutingID(), + base::Unretained(&defer))); + + // Wait for the request to finish processing. + message_loop_.Run(); +} + +TEST_F(InterceptNavigationResourceThrottleTest, + CallbackIsPostFalseForGet) { + bool defer = false; + + EXPECT_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation(_, Ne(GURL(kUnsafeTestUrl)), _, false, _, + _)) + .WillOnce(Return(false)); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind( + &InterceptNavigationResourceThrottleTest:: + RunThrottleWillStartRequestOnIOThread, + base::Unretained(this), + GURL(kTestUrl), + "GET", + web_contents()->GetRenderViewHost()->GetProcess()->GetID(), + web_contents()->GetRenderViewHost()->GetRoutingID(), + base::Unretained(&defer))); + + // Wait for the request to finish processing. + message_loop_.Run(); +} + +TEST_F(InterceptNavigationResourceThrottleTest, + CallbackIsPostTrueForPost) { + bool defer = false; + + EXPECT_CALL(*mock_callback_receiver_, + ShouldIgnoreNavigation(_, Ne(GURL(kUnsafeTestUrl)), _, true, _, + _)) + .WillOnce(Return(false)); + + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind( + &InterceptNavigationResourceThrottleTest:: + RunThrottleWillStartRequestOnIOThread, + base::Unretained(this), + GURL(kTestUrl), + "POST", + web_contents()->GetRenderViewHost()->GetProcess()->GetID(), + web_contents()->GetRenderViewHost()->GetRoutingID(), + base::Unretained(&defer))); + + // Wait for the request to finish processing. + message_loop_.Run(); +} + +} // namespace components |