summaryrefslogtreecommitdiffstats
path: root/components/navigation_interception
diff options
context:
space:
mode:
authorkaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-23 03:55:16 +0000
committerkaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-23 03:55:16 +0000
commit62885abfdaced9bad3f119e5f4f15b81f34c02c6 (patch)
tree97eba6bb62094c692561f9355d037a392e72e54f /components/navigation_interception
parent62c607121a2c0b5bf463cd6da0fd568140523e30 (diff)
downloadchromium_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')
-rw-r--r--components/navigation_interception/DEPS8
-rw-r--r--components/navigation_interception/OWNERS3
-rw-r--r--components/navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java24
-rw-r--r--components/navigation_interception/component_jni_registrar.cc22
-rw-r--r--components/navigation_interception/component_jni_registrar.h18
-rw-r--r--components/navigation_interception/intercept_navigation_delegate.cc116
-rw-r--r--components/navigation_interception/intercept_navigation_delegate.h69
-rw-r--r--components/navigation_interception/intercept_navigation_resource_throttle.cc138
-rw-r--r--components/navigation_interception/intercept_navigation_resource_throttle.h63
-rw-r--r--components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc414
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