summaryrefslogtreecommitdiff
path: root/chromium/components/navigation_interception
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/navigation_interception')
-rw-r--r--chromium/components/navigation_interception/DEPS8
-rw-r--r--chromium/components/navigation_interception/OWNERS2
-rw-r--r--chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java21
-rw-r--r--chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java36
-rw-r--r--chromium/components/navigation_interception/component_jni_registrar.cc24
-rw-r--r--chromium/components/navigation_interception/component_jni_registrar.h18
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_delegate.cc107
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_delegate.h69
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_resource_throttle.cc140
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_resource_throttle.h62
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc475
-rw-r--r--chromium/components/navigation_interception/navigation_params.cc42
-rw-r--r--chromium/components/navigation_interception/navigation_params.h47
-rw-r--r--chromium/components/navigation_interception/navigation_params_android.cc34
-rw-r--r--chromium/components/navigation_interception/navigation_params_android.h22
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_