summaryrefslogtreecommitdiffstats
path: root/components
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
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')
-rw-r--r--components/DEPS1
-rw-r--r--components/components.gyp1
-rw-r--r--components/components_tests.gypi38
-rw-r--r--components/navigation_interception.gypi76
-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
-rw-r--r--components/test/DEPS4
-rw-r--r--components/test/run_all_unittests.cc40
16 files changed, 1021 insertions, 14 deletions
diff --git a/components/DEPS b/components/DEPS
index eb8bee4..d6fbc7c 100644
--- a/components/DEPS
+++ b/components/DEPS
@@ -13,5 +13,4 @@ include_rules = [
# "+content/public/browser" rule.
"-content",
"+content/public/common",
- "+content/public/test",
]
diff --git a/components/components.gyp b/components/components.gyp
index a55fe44..eea034a 100644
--- a/components/components.gyp
+++ b/components/components.gyp
@@ -6,6 +6,7 @@
'includes': [
'auto_login_parser.gypi',
'components_tests.gypi',
+ 'navigation_interception.gypi',
'visitedlink.gypi',
'web_contents_delegate_android.gypi',
],
diff --git a/components/components_tests.gypi b/components/components_tests.gypi
index 537a8c0..ef40684 100644
--- a/components/components_tests.gypi
+++ b/components/components_tests.gypi
@@ -3,17 +3,31 @@
# found in the LICENSE file.
{
- 'targets': [
- {
- 'target_name': 'components_unittests',
- 'type': '<(gtest_target_type)',
- 'sources': [
- 'test/run_all_unittests.cc',
- ],
- 'dependencies': [
- '../base/base.gyp:test_support_base',
- '../testing/gtest.gyp:gtest',
+ 'conditions': [
+ ['OS != "ios"', {
+ 'targets': [
+ {
+ 'target_name': 'components_unittests',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'navigation_interception/intercept_navigation_resource_throttle_unittest.cc',
+ 'test/run_all_unittests.cc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:test_support_base',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+
+ # Dependencies of intercept_navigation_resource_throttle_unittest.cc
+ '../content/content.gyp:test_support_content',
+ '../skia/skia.gyp:skia',
+ 'navigation_interception',
+ ],
+ }
],
- }
- ]
+ }],
+ ],
}
diff --git a/components/navigation_interception.gypi b/components/navigation_interception.gypi
new file mode 100644
index 0000000..4912929
--- /dev/null
+++ b/components/navigation_interception.gypi
@@ -0,0 +1,76 @@
+# 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.
+
+{
+ 'conditions': [
+ ['OS != "ios"', {
+ 'targets': [
+ {
+ 'target_name': 'navigation_interception',
+ 'type': 'static_library',
+ 'defines!': ['CONTENT_IMPLEMENTATION'],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../content/content.gyp:content_common',
+ '../net/net.gyp:net',
+ ],
+ 'include_dirs': [
+ '..',
+ '../skia/config',
+ '<(SHARED_INTERMEDIATE_DIR)/navigation_interception',
+
+ ],
+ 'sources': [
+ 'navigation_interception/intercept_navigation_resource_throttle.cc',
+ 'navigation_interception/intercept_navigation_resource_throttle.h',
+ ],
+ 'conditions': [
+ ['OS=="android"', {
+ 'dependencies': [
+ 'navigation_interception_jni_headers',
+ ],
+ 'sources': [
+ 'navigation_interception/component_jni_registrar.cc',
+ 'navigation_interception/component_jni_registrar.h',
+ 'navigation_interception/intercept_navigation_delegate.cc',
+ 'navigation_interception/intercept_navigation_delegate.h',
+ ],
+ }],
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'navigation_interception_java',
+ 'type': 'none',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'variables': {
+ 'package_name': 'navigation_interception',
+ 'java_in_dir': 'navigation_interception/android/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'navigation_interception_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'navigation_interception/android/java/src/org/chromium/content/components/navigation_interception/InterceptNavigationDelegate.java',
+ ],
+ 'variables': {
+ 'jni_gen_dir': 'navigation_interception',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ ],
+ }],
+ ],
+ }],
+ ],
+}
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
diff --git a/components/test/DEPS b/components/test/DEPS
new file mode 100644
index 0000000..c220939
--- /dev/null
+++ b/components/test/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/test",
+ "+ui/compositor",
+]
diff --git a/components/test/run_all_unittests.cc b/components/test/run_all_unittests.cc
index 2c8d29c..49032ba 100644
--- a/components/test/run_all_unittests.cc
+++ b/components/test/run_all_unittests.cc
@@ -2,8 +2,46 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/scoped_ptr.h"
#include "base/test/test_suite.h"
+#include "content/public/test/test_content_client_initializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/compositor/compositor_setup.h"
+
+namespace components {
+
+class ComponentsUnitTestEventListener : public testing::EmptyTestEventListener {
+ public:
+ ComponentsUnitTestEventListener() {}
+ virtual ~ComponentsUnitTestEventListener() {}
+
+ virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE {
+ content_initializer_.reset(new content::TestContentClientInitializer());
+ }
+
+ virtual void OnTestEnd(const testing::TestInfo& test_info) OVERRIDE {
+ content_initializer_.reset();
+ }
+
+ private:
+ scoped_ptr<content::TestContentClientInitializer> content_initializer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ComponentsUnitTestEventListener);
+};
+
+} // namespace components
int main(int argc, char** argv) {
- return base::TestSuite(argc, argv).Run();
+ base::TestSuite test_suite(argc, argv);
+
+ // Mock out the compositor on platforms that use it.
+ ui::SetupTestCompositor();
+
+ // The listener will set up common test environment for all components unit
+ // tests.
+ testing::TestEventListeners& listeners =
+ testing::UnitTest::GetInstance()->listeners();
+ listeners.Append(new components::ComponentsUnitTestEventListener());
+
+ return test_suite.Run();
}