diff options
author | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-16 16:28:51 +0000 |
---|---|---|
committer | mkosiba@chromium.org <mkosiba@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-16 16:28:51 +0000 |
commit | 5b15e13c68d8ca8165698d50410557f44b012b31 (patch) | |
tree | 08ce7da669bd4a0eb41b3b0fa0ca2f9e0b9040b7 /android_webview | |
parent | 81b9ab27d60b4645099c14e6a45eb66a59c5137e (diff) | |
download | chromium_src-5b15e13c68d8ca8165698d50410557f44b012b31.zip chromium_src-5b15e13c68d8ca8165698d50410557f44b012b31.tar.gz chromium_src-5b15e13c68d8ca8165698d50410557f44b012b31.tar.bz2 |
Implement android_webview url intercepting.
This implements the WebViewClient shouldInterceptRequest and onLoadResource callbacks.
BUG=138481
Android-only change - ran through android try.
NOTRY=true
Review URL: https://chromiumcodereview.appspot.com/11110013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162141 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'android_webview')
24 files changed, 864 insertions, 161 deletions
diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp index 3ff9aa8..78f002b 100644 --- a/android_webview/android_webview.gyp +++ b/android_webview/android_webview.gyp @@ -38,8 +38,11 @@ 'browser/aw_http_auth_handler_base.h', 'browser/aw_login_delegate.cc', 'browser/aw_login_delegate.h', + 'browser/aw_request_interceptor.cc', + 'browser/aw_request_interceptor.h', 'browser/find_helper.cc', 'browser/find_helper.h', + 'browser/intercepted_request_data.h', 'browser/net/aw_network_delegate.cc', 'browser/net/aw_network_delegate.h', 'browser/net_disk_cache_remover.cc', diff --git a/android_webview/browser/aw_contents_io_thread_client.h b/android_webview/browser/aw_contents_io_thread_client.h index 659be10..0dd4146 100644 --- a/android_webview/browser/aw_contents_io_thread_client.h +++ b/android_webview/browser/aw_contents_io_thread_client.h @@ -7,7 +7,7 @@ #include "base/memory/scoped_ptr.h" -class InterceptedRequestData; +class GURL; namespace net { class URLRequest; @@ -15,6 +15,8 @@ class URLRequest; namespace android_webview { +class InterceptedRequestData; + // This class provides a means of calling Java methods on an instance that has // a 1:1 relationship with a WebContents instance directly from the IO thread. // @@ -43,6 +45,7 @@ class AwContentsIoThreadClient { // This method is called on the IO thread only. virtual scoped_ptr<InterceptedRequestData> ShouldInterceptRequest( + const GURL& location, const net::URLRequest* request) = 0; // Retrieve the AllowContentAccess setting value of this AwContents. diff --git a/android_webview/browser/aw_request_interceptor.cc b/android_webview/browser/aw_request_interceptor.cc new file mode 100644 index 0000000..c48a090 --- /dev/null +++ b/android_webview/browser/aw_request_interceptor.cc @@ -0,0 +1,137 @@ +// 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 "android_webview/browser/aw_request_interceptor.h" + +#include "android_webview/browser/aw_contents_io_thread_client.h" +#include "android_webview/browser/intercepted_request_data.h" +#include "base/android/jni_string.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/resource_request_info.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" +#include "net/url_request/url_request_job.h" + +using content::BrowserThread; +using content::RenderViewHost; +using content::ResourceRequestInfo; + +namespace android_webview { + +namespace { + +const void* kURLRequestUserDataKey = &kURLRequestUserDataKey; + +class URLRequestUserData : public base::SupportsUserData::Data { + public: + URLRequestUserData( + scoped_ptr<InterceptedRequestData> intercepted_request_data) + : intercepted_request_data_(intercepted_request_data.Pass()) { + } + + static URLRequestUserData* Get(net::URLRequest* request) { + return reinterpret_cast<URLRequestUserData*>( + request->GetUserData(kURLRequestUserDataKey)); + } + + const InterceptedRequestData* intercepted_request_data() const { + return intercepted_request_data_.get(); + } + + private: + scoped_ptr<InterceptedRequestData> intercepted_request_data_; +}; + +} // namespace + +AwRequestInterceptor::AwRequestInterceptor() { +} + +AwRequestInterceptor::~AwRequestInterceptor() { +} + +scoped_ptr<InterceptedRequestData> +AwRequestInterceptor::QueryForInterceptedRequestData( + const GURL& location, + net::URLRequest* request) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + int render_process_id, render_view_id; + if (!ResourceRequestInfo::GetRenderViewForRequest( + request, &render_process_id, &render_view_id)) + return scoped_ptr<InterceptedRequestData>(); + + scoped_ptr<AwContentsIoThreadClient> io_thread_client = + AwContentsIoThreadClient::FromID(render_process_id, render_view_id); + + if (!io_thread_client.get()) + return scoped_ptr<InterceptedRequestData>(); + + return io_thread_client->ShouldInterceptRequest(location, request).Pass(); +} + +net::URLRequestJob* AwRequestInterceptor::MaybeInterceptInternal( + const GURL& location, + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + // See if we've already found out the intercepted_request_data for this + // request. + // This is done not only for efficiency reasons, but also for correctness + // as it is possible for the Interceptor chain to be invoked more than once + // (in which case we don't want to query the embedder multiple times). + URLRequestUserData* user_data = URLRequestUserData::Get(request); + + if (!user_data) { + // To ensure we only query the embedder once, we rely on the fact that the + // user_data object will be created and attached to the URLRequest after a + // call to QueryForInterceptedRequestData is made (regardless of whether + // the result of that call is a valid InterceptedRequestData* pointer or + // NULL. + user_data = new URLRequestUserData( + QueryForInterceptedRequestData(location, request)); + request->SetUserData(kURLRequestUserDataKey, user_data); + } + + const InterceptedRequestData* intercepted_request_data = + user_data->intercepted_request_data(); + + if (!intercepted_request_data) + return NULL; + return intercepted_request_data->CreateJobFor(request, network_delegate); +} + +net::URLRequestJob* AwRequestInterceptor::MaybeIntercept( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const { + return MaybeInterceptInternal(request->url(), request, network_delegate); +} + +net::URLRequestJob* AwRequestInterceptor::MaybeInterceptRedirect( + const GURL& location, + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const { + return MaybeInterceptInternal(location, request, network_delegate); +} + +net::URLRequestJob* AwRequestInterceptor::MaybeInterceptResponse( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const { + return NULL; +} + +//static +void AwRequestInterceptor::RegisterInterceptorOnIOThread( + net::URLRequestContextGetter* context_getter) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // The job factory takes ownership of the interceptor. + const_cast<net::URLRequestJobFactory*>( + context_getter->GetURLRequestContext()->job_factory())->AddInterceptor( + new AwRequestInterceptor()); +} + +} // namespace android_webview diff --git a/android_webview/browser/aw_request_interceptor.h b/android_webview/browser/aw_request_interceptor.h new file mode 100644 index 0000000..ebafad9 --- /dev/null +++ b/android_webview/browser/aw_request_interceptor.h @@ -0,0 +1,61 @@ +// 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 ANDROID_WEBVIEW_BROWSER_AW_REQUEST_INTERCEPTOR_H_ +#define ANDROID_WEBVIEW_BROWSER_AW_REQUEST_INTERCEPTOR_H_ + +#include "base/memory/scoped_ptr.h" +#include "net/url_request/url_request_job_factory.h" + +class GURL; + +namespace net { +class URLRequest; +class URLRequestContextGetter; +class URLRequestJob; +class NetworkDelegate; +} + +namespace android_webview { + +class InterceptedRequestData; + +// This class allows the Java-side embedder to substitute the default +// URLRequest of a given request for an alternative job that will read data +// from a Java stream. +class AwRequestInterceptor : public net::URLRequestJobFactory::Interceptor { + public: + AwRequestInterceptor(); + virtual ~AwRequestInterceptor(); + + static void RegisterInterceptorOnIOThread( + net::URLRequestContextGetter* context_getter); + + // net::URLRequestJobFactory::Interceptor overrides ------------------------- + virtual net::URLRequestJob* MaybeIntercept( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const OVERRIDE; + virtual net::URLRequestJob* MaybeInterceptRedirect( + const GURL& location, + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const OVERRIDE; + virtual net::URLRequestJob* MaybeInterceptResponse( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const OVERRIDE; + + private: + net::URLRequestJob* MaybeInterceptInternal( + const GURL& location, + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const; + scoped_ptr<InterceptedRequestData> QueryForInterceptedRequestData( + const GURL& location, + net::URLRequest* request) const; + + DISALLOW_COPY_AND_ASSIGN(AwRequestInterceptor); +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_AW_REQUEST_INTERCEPTOR_H_ diff --git a/android_webview/browser/intercepted_request_data.h b/android_webview/browser/intercepted_request_data.h new file mode 100644 index 0000000..672833c --- /dev/null +++ b/android_webview/browser/intercepted_request_data.h @@ -0,0 +1,43 @@ +// 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 ANDROID_WEBVIEW_BROWSER_INTERCEPTED_REQUEST_DATA_H_ +#define ANDROID_WEBVIEW_BROWSER_INTERCEPTED_REQUEST_DATA_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" + +namespace net { +class URLRequest; +class URLRequestJob; +class NetworkDelegate; +} + +namespace android_webview { + +// This class represents the Java-side data that is to be used to complete a +// particular URLRequest. +class InterceptedRequestData { + public: + virtual ~InterceptedRequestData() {} + + // This creates a URLRequestJob for the |request| wich will read data from + // the |intercepted_request_data| structure (instead of going to the network + // or to the cache). + // The newly created job does not take ownership of |this|. + virtual net::URLRequestJob* CreateJobFor( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const = 0; + + protected: + InterceptedRequestData() {} + + private: + DISALLOW_COPY_AND_ASSIGN(InterceptedRequestData); +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_INTERCEPTED_REQUEST_DATA_H_ diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 476fd78..36511fd 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -7,6 +7,8 @@ package org.chromium.android_webview; import android.graphics.Bitmap; import android.net.http.SslCertificate; import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.text.TextUtils; import android.util.Log; @@ -46,6 +48,7 @@ public class AwContents { private static final String WEB_ARCHIVE_EXTENSION = ".mht"; + private int mNativeAwContents; private ContentViewCore mContentViewCore; private AwContentsClient mContentsClient; @@ -53,6 +56,7 @@ public class AwContents { private InterceptNavigationDelegateImpl mInterceptNavigationDelegate; // This can be accessed on any thread after construction. See AwContentsIoThreadClient. private final AwSettings mSettings; + private final IoThreadClientHandler mIoThreadClientHandler; private static final class DestroyRunnable implements Runnable { private int mNativeAwContents; @@ -67,11 +71,33 @@ public class AwContents { private CleanupReference mCleanupReference; + private class IoThreadClientHandler extends Handler { + public static final int MSG_SHOULD_INTERCEPT_REQUEST = 1; + + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case MSG_SHOULD_INTERCEPT_REQUEST: + final String url = (String)msg.obj; + AwContents.this.mContentsClient.onLoadResource(url); + break; + } + } + } + private class IoThreadClientImpl implements AwContentsIoThreadClient { // Called on the IO thread. @Override - public InterceptedRequestData shouldInterceptRequest(String url) { - return AwContents.this.mContentsClient.shouldInterceptRequest(url); + public InterceptedRequestData shouldInterceptRequest(final String url) { + InterceptedRequestData interceptedRequestData = + AwContents.this.mContentsClient.shouldInterceptRequest(url); + if (interceptedRequestData == null) { + mIoThreadClientHandler.sendMessage( + mIoThreadClientHandler.obtainMessage( + IoThreadClientHandler.MSG_SHOULD_INTERCEPT_REQUEST, + url)); + } + return interceptedRequestData; } // Called on the IO thread. @@ -132,6 +158,7 @@ public class AwContents { mContentViewCore = contentViewCore; mContentsClient = contentsClient; mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents)); + mIoThreadClientHandler = new IoThreadClientHandler(); mContentViewCore.initialize(containerView, internalAccessAdapter, nativeGetWebContents(mNativeAwContents), nativeWindow, diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java index f646040..87fda07 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java @@ -142,6 +142,8 @@ public abstract class AwContentsClient extends ContentViewClient { public abstract InterceptedRequestData shouldInterceptRequest(String url); + public abstract void onLoadResource(String url); + public abstract boolean shouldIgnoreNavigation(String url); public abstract void onUnhandledKeyEvent(KeyEvent event); diff --git a/android_webview/java/src/org/chromium/android_webview/InterceptedRequestData.java b/android_webview/java/src/org/chromium/android_webview/InterceptedRequestData.java index 2a5460e..f6f3d9d 100644 --- a/android_webview/java/src/org/chromium/android_webview/InterceptedRequestData.java +++ b/android_webview/java/src/org/chromium/android_webview/InterceptedRequestData.java @@ -5,12 +5,14 @@ package org.chromium.android_webview; import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; import java.io.InputStream; /** * The response information that is to be returned for a particular resource fetch. */ +@JNINamespace("android_webview") public class InterceptedRequestData { private String mMimeType; private String mCharset; diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java index 9d2f5c4..dd043ef 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidWebViewTestBase.java @@ -13,6 +13,7 @@ import junit.framework.Assert; import org.chromium.android_webview.AwContents; import org.chromium.android_webview.AwContentsClient; import org.chromium.android_webview.AwSettings; +import org.chromium.android_webview.test.util.JSUtils; import org.chromium.content.browser.ContentSettings; import org.chromium.content.browser.ContentView; import org.chromium.content.browser.ContentViewCore; @@ -233,19 +234,8 @@ public class AndroidWebViewTestBase */ protected String executeJavaScriptAndWaitForResult(final AwContents awContents, TestAwContentsClient viewClient, final String code) throws Throwable { - final AtomicInteger requestId = new AtomicInteger(); - TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper - onEvaluateJavaScriptResultHelper = viewClient.getOnEvaluateJavaScriptResultHelper(); - int currentCallCount = onEvaluateJavaScriptResultHelper.getCallCount(); - runTestOnUiThread(new Runnable() { - @Override - public void run() { - requestId.set(awContents.getContentViewCore().evaluateJavaScript(code)); - } - }); - onEvaluateJavaScriptResultHelper.waitForCallback(currentCallCount); - Assert.assertEquals("Response ID mismatch when evaluating JavaScript.", - requestId.get(), onEvaluateJavaScriptResultHelper.getId()); - return onEvaluateJavaScriptResultHelper.getJsonResult(); + return JSUtils.executeJavaScriptAndWaitForResult(this, awContents, + viewClient.getOnEvaluateJavaScriptResultHelper(), + code); } } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwShouldIgnoreNavigationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldIgnoreNavigationTest.java index dd1fd4c..3fc4d1a 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwShouldIgnoreNavigationTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldIgnoreNavigationTest.java @@ -12,6 +12,8 @@ import android.view.MotionEvent; import android.util.Log; import org.chromium.android_webview.AwContents; +import org.chromium.android_webview.test.util.CommonResources; +import org.chromium.android_webview.test.util.JSUtils; import org.chromium.android_webview.test.util.TestWebServer; import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; @@ -31,10 +33,11 @@ import java.util.concurrent.Callable; /** * Tests for the WebViewClient.shouldOverrideUrlLoading() method. */ -public class AwShouldIgnoreNavigationTest extends AndroidWebViewTestBase { +public class AwContentsClientShouldIgnoreNavigationTest extends AndroidWebViewTestBase { private final static String ABOUT_BLANK_URL = "about:blank"; private final static String DATA_URL = "data:text/html,<div/>"; private final static String REDIRECT_TARGET_PATH = "/redirect_target.html"; + private final static String TITLE = "TITLE"; private static final long TEST_TIMEOUT = 20000L; private static final int CHECK_INTERVAL = 100; @@ -85,7 +88,6 @@ public class AwShouldIgnoreNavigationTest extends AndroidWebViewTestBase { private ShouldIgnoreNavigationHelper mShouldIgnoreNavigationHelper; public TestAwContentsClient() { - super(); mShouldIgnoreNavigationHelper = new ShouldIgnoreNavigationHelper(); } @@ -97,32 +99,8 @@ public class AwShouldIgnoreNavigationTest extends AndroidWebViewTestBase { private void clickOnLinkUsingJs(final AwContents awContents, final TestAwContentsClient contentsClient) throws Throwable { enableJavaScriptOnUiThread(awContents); - - assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { - @Override - public boolean isSatisfied() { - try { - String linkIsNotNull = executeJavaScriptAndWaitForResult(awContents, - contentsClient, "document.getElementById('link') != null"); - return linkIsNotNull.equals("true"); - } catch (Throwable t) { - t.printStackTrace(); - fail("Failed to check if DOM is loaded: " + t.toString()); - return false; - } - } - }, WAIT_TIMEOUT_SECONDS * 1000, CHECK_INTERVAL)); - - runTestOnUiThread(new Runnable() { - @Override - public void run() { - awContents.getContentViewCore().evaluateJavaScript( - "var evObj = document.createEvent('Events'); " + - "evObj.initEvent('click', true, false); " + - "document.getElementById('link').dispatchEvent(evObj);" + - "console.log('element with id link clicked');"); - } - }); + JSUtils.clickOnLinkUsingJs(this, awContents, + contentsClient.getOnEvaluateJavaScriptResultHelper(), "link"); } // Since this value is read on the UI thread, it's simpler to set it there too. @@ -138,21 +116,7 @@ public class AwShouldIgnoreNavigationTest extends AndroidWebViewTestBase { } private String makeHtmlPageFrom(String headers, String body) { - return "<html>" + - "<head>" + - "<title>Title</title>" + - "<style type=\"text/css\">" + - // Make the image take up all of the page so that we don't have to do - // any fancy hit target calculations when synthesizing the touch event - // to click it. - "img.big { width:100%; height:100%; background-color:blue; }" + - "</style>" + - headers + - "</head>" + - "<body>" + - body + - "</body>" + - "</html>"; + return CommonResources.makeHtmlPageFrom("<title>" + TITLE + "</title> " + headers, body); } private String getHtmlForPageWithSimpleLinkTo(String destination) { @@ -241,7 +205,7 @@ public class AwShouldIgnoreNavigationTest extends AndroidWebViewTestBase { loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(), getHtmlForPageWithSimpleLinkTo(DATA_URL), "text/html", false); - assertEquals("Title", getTitleOnUiThread(awContents)); + assertEquals(TITLE, getTitleOnUiThread(awContents)); } /** diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java new file mode 100644 index 0000000..f5b4546 --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java @@ -0,0 +1,308 @@ +// 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.android_webview.test; + +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; +import android.util.Pair; + +import org.chromium.android_webview.AwContents; +import org.chromium.android_webview.InterceptedRequestData; +import org.chromium.android_webview.test.util.CommonResources; +import org.chromium.android_webview.test.util.JSUtils; +import org.chromium.android_webview.test.util.TestWebServer; +import org.chromium.base.test.util.Feature; +import org.chromium.content.browser.test.util.CallbackHelper; +import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper; +import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageStartedHelper; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Tests for the WebViewClient.shouldInterceptRequest() method. + */ +public class AwContentsClientShouldInterceptRequestTest extends AndroidWebViewTestBase { + + private static class TestAwContentsClient + extends org.chromium.android_webview.test.TestAwContentsClient { + + public static class ShouldInterceptRequestHelper extends CallbackHelper { + private List<String> mShouldInterceptRequestUrls = new ArrayList<String>(); + // This is read from the IO thread, so needs to be marked volatile. + private volatile InterceptedRequestData mShouldInterceptRequestReturnValue = null; + void setReturnValue(InterceptedRequestData value) { + mShouldInterceptRequestReturnValue = value; + } + public List<String> getUrls() { + assert getCallCount() > 0; + return mShouldInterceptRequestUrls; + } + public InterceptedRequestData getReturnValue() { + return mShouldInterceptRequestReturnValue; + } + public void notifyCalled(String url) { + mShouldInterceptRequestUrls.add(url); + notifyCalled(); + } + } + + public static class OnLoadResourceHelper extends CallbackHelper { + private String mUrl; + + public String getUrl() { + assert getCallCount() > 0; + return mUrl; + } + + public void notifyCalled(String url) { + mUrl = url; + notifyCalled(); + } + } + + @Override + public InterceptedRequestData shouldInterceptRequest(String url) { + InterceptedRequestData returnValue = mShouldInterceptRequestHelper.getReturnValue(); + mShouldInterceptRequestHelper.notifyCalled(url); + return returnValue; + } + + @Override + public void onLoadResource(String url) { + super.onLoadResource(url); + mOnLoadResourceHelper.notifyCalled(url); + } + + private ShouldInterceptRequestHelper mShouldInterceptRequestHelper; + private OnLoadResourceHelper mOnLoadResourceHelper; + + public TestAwContentsClient() { + mShouldInterceptRequestHelper = new ShouldInterceptRequestHelper(); + mOnLoadResourceHelper = new OnLoadResourceHelper(); + } + + public ShouldInterceptRequestHelper getShouldInterceptRequestHelper() { + return mShouldInterceptRequestHelper; + } + + public OnLoadResourceHelper getOnLoadResourceHelper() { + return mOnLoadResourceHelper; + } + } + + private String addPageToTestServer(TestWebServer webServer, String httpPath, String html) { + List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>(); + headers.add(Pair.create("Content-Type", "text/html")); + headers.add(Pair.create("Cache-Control", "no-store")); + return webServer.setResponse(httpPath, html, headers); + } + + private String addAboutPageToTestServer(TestWebServer webServer) { + return addPageToTestServer(webServer, "/" + CommonResources.ABOUT_FILENAME, + CommonResources.ABOUT_HTML); + } + + private InterceptedRequestData stringToInterceptedRequestData(String input) throws Throwable { + final String mimeType = "text/html"; + final String encoding = "UTF-8"; + + return new InterceptedRequestData( + mimeType, encoding, new ByteArrayInputStream(input.getBytes(encoding))); + } + + private TestWebServer mWebServer; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mWebServer = new TestWebServer(false); + } + + @Override + protected void tearDown() throws Exception { + mWebServer.shutdown(); + super.tearDown(); + } + + @SmallTest + @Feature({"Android-WebView"}) + public void testCalledWithCorrectUrl() throws Throwable { + final TestAwContentsClient contentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(contentsClient); + final AwContents awContents = testContainerView.getAwContents(); + final TestAwContentsClient.ShouldInterceptRequestHelper shouldInterceptRequestHelper = + contentsClient.getShouldInterceptRequestHelper(); + + final String aboutPageUrl = addAboutPageToTestServer(mWebServer); + + int callCount = shouldInterceptRequestHelper.getCallCount(); + int onPageFinishedCallCount = contentsClient.getOnPageFinishedHelper().getCallCount(); + + loadUrlAsync(awContents, aboutPageUrl); + + shouldInterceptRequestHelper.waitForCallback(callCount); + assertEquals(1, shouldInterceptRequestHelper.getUrls().size()); + assertEquals(aboutPageUrl, + shouldInterceptRequestHelper.getUrls().get(0)); + + contentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount); + assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread(awContents)); + } + + @SmallTest + @Feature({"Android-WebView"}) + public void testOnLoadResourceCalledWithCorrectUrl() throws Throwable { + final TestAwContentsClient contentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(contentsClient); + final AwContents awContents = testContainerView.getAwContents(); + final TestAwContentsClient.OnLoadResourceHelper onLoadResourceHelper = + contentsClient.getOnLoadResourceHelper(); + + final String aboutPageUrl = addAboutPageToTestServer(mWebServer); + + int callCount = onLoadResourceHelper.getCallCount(); + int onPageFinishedCallCount = contentsClient.getOnPageFinishedHelper().getCallCount(); + + loadUrlAsync(awContents, aboutPageUrl); + + onLoadResourceHelper.waitForCallback(callCount); + assertEquals(aboutPageUrl, onLoadResourceHelper.getUrl()); + } + + @SmallTest + @Feature({"Android-WebView"}) + public void testDoesNotCrashOnInvalidData() throws Throwable { + final TestAwContentsClient contentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(contentsClient); + final AwContents awContents = testContainerView.getAwContents(); + final TestAwContentsClient.ShouldInterceptRequestHelper shouldInterceptRequestHelper = + contentsClient.getShouldInterceptRequestHelper(); + + final String aboutPageUrl = addAboutPageToTestServer(mWebServer); + + shouldInterceptRequestHelper.setReturnValue( + new InterceptedRequestData("text/html", "UTF-8", null)); + int callCount = shouldInterceptRequestHelper.getCallCount(); + loadUrlAsync(awContents, aboutPageUrl); + shouldInterceptRequestHelper.waitForCallback(callCount); + + shouldInterceptRequestHelper.setReturnValue( + new InterceptedRequestData(null, null, new ByteArrayInputStream(new byte[0]))); + callCount = shouldInterceptRequestHelper.getCallCount(); + loadUrlAsync(awContents, aboutPageUrl); + shouldInterceptRequestHelper.waitForCallback(callCount); + + shouldInterceptRequestHelper.setReturnValue( + new InterceptedRequestData(null, null, null)); + callCount = shouldInterceptRequestHelper.getCallCount(); + loadUrlAsync(awContents, aboutPageUrl); + shouldInterceptRequestHelper.waitForCallback(callCount); + } + + private String makePageWithTitle(String title) { + return CommonResources.makeHtmlPageFrom("<title>" + title + "</title>", + "<div> The title is: " + title + " </div>"); + } + + @SmallTest + @Feature({"Android-WebView"}) + public void testCanInterceptMainFrame() throws Throwable { + final TestAwContentsClient contentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(contentsClient); + final AwContents awContents = testContainerView.getAwContents(); + final TestAwContentsClient.ShouldInterceptRequestHelper shouldInterceptRequestHelper = + contentsClient.getShouldInterceptRequestHelper(); + + final String expectedTitle = "testShouldInterceptRequestCanInterceptMainFrame"; + final String expectedPage = makePageWithTitle(expectedTitle); + + shouldInterceptRequestHelper.setReturnValue( + stringToInterceptedRequestData(expectedPage)); + + final String aboutPageUrl = addAboutPageToTestServer(mWebServer); + + loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), aboutPageUrl); + + assertEquals(expectedTitle, getTitleOnUiThread(awContents)); + assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME)); + } + + @SmallTest + @Feature({"Android-WebView"}) + public void testDoesNotChangeReportedUrl() throws Throwable { + final TestAwContentsClient contentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(contentsClient); + final AwContents awContents = testContainerView.getAwContents(); + final TestAwContentsClient.ShouldInterceptRequestHelper shouldInterceptRequestHelper = + contentsClient.getShouldInterceptRequestHelper(); + + shouldInterceptRequestHelper.setReturnValue( + stringToInterceptedRequestData(makePageWithTitle("some title"))); + + final String aboutPageUrl = addAboutPageToTestServer(mWebServer); + + loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), aboutPageUrl); + + assertEquals(aboutPageUrl, contentsClient.getOnPageFinishedHelper().getUrl()); + assertEquals(aboutPageUrl, contentsClient.getOnPageStartedHelper().getUrl()); + } + + + @SmallTest + @Feature({"Android-WebView"}) + public void testCalledForImage() throws Throwable { + final TestAwContentsClient contentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(contentsClient); + final AwContents awContents = testContainerView.getAwContents(); + final TestAwContentsClient.ShouldInterceptRequestHelper shouldInterceptRequestHelper = + contentsClient.getShouldInterceptRequestHelper(); + + final String imagePath = "/" + CommonResources.FAVICON_FILENAME; + final String imageUrl = mWebServer.setResponseBase64(imagePath, + CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true)); + final String pageWithImage = + addPageToTestServer(mWebServer, "/page_with_image.html", + CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME)); + + int callCount = shouldInterceptRequestHelper.getCallCount(); + loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), pageWithImage); + shouldInterceptRequestHelper.waitForCallback(callCount, 2); + + assertEquals(2, shouldInterceptRequestHelper.getUrls().size()); + assertTrue(shouldInterceptRequestHelper.getUrls().get(1).endsWith( + CommonResources.FAVICON_FILENAME)); + } + + @SmallTest + @Feature({"Android-WebView"}) + public void testCalledForIframe() throws Throwable { + final TestAwContentsClient contentsClient = new TestAwContentsClient(); + final AwTestContainerView testContainerView = + createAwTestContainerViewOnMainSync(contentsClient); + final AwContents awContents = testContainerView.getAwContents(); + final TestAwContentsClient.ShouldInterceptRequestHelper shouldInterceptRequestHelper = + contentsClient.getShouldInterceptRequestHelper(); + + final String aboutPageUrl = addAboutPageToTestServer(mWebServer); + final String pageWithIframe = addPageToTestServer(mWebServer, "/page_with_iframe.html", + CommonResources.makeHtmlPageFrom("", + "<iframe src=\"" + aboutPageUrl + "\"/>")); + + int callCount = shouldInterceptRequestHelper.getCallCount(); + loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), pageWithIframe); + shouldInterceptRequestHelper.waitForCallback(callCount, 2); + assertEquals(2, shouldInterceptRequestHelper.getUrls().size()); + assertEquals(aboutPageUrl, shouldInterceptRequestHelper.getUrls().get(1)); + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java b/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java index 9c4bd57..2760c2d 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/NullContentsClient.java @@ -38,6 +38,10 @@ class NullContentsClient extends AwContentsClient { } @Override + public void onLoadResource(String url) { + } + + @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { return false; } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/CommonResources.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/CommonResources.java index dff151b..f7ccb0f 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/util/CommonResources.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/CommonResources.java @@ -85,4 +85,21 @@ public class CommonResources { " This is the Google!" + " </body>" + "</html>"; + + public static String makeHtmlPageFrom(String headers, String body) { + return "<html>" + + "<head>" + + "<style type=\"text/css\">" + + // Make the image take up all of the page so that we don't have to do + // any fancy hit target calculations when synthesizing the touch event + // to click it. + "img.big { width:100%; height:100%; background-color:blue; }" + + "</style>" + + headers + + "</head>" + + "<body>" + + body + + "</body>" + + "</html>"; + } } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/util/JSUtils.java b/android_webview/javatests/src/org/chromium/android_webview/test/util/JSUtils.java new file mode 100644 index 0000000..33956a0 --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/util/JSUtils.java @@ -0,0 +1,77 @@ +// 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.android_webview.test.util; + +import android.test.InstrumentationTestCase; +import android.util.Log; + +import junit.framework.Assert; + +import org.chromium.android_webview.AwContents; +import org.chromium.content.browser.test.util.CallbackHelper; +import org.chromium.content.browser.test.util.Criteria; +import org.chromium.content.browser.test.util.CriteriaHelper; +import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper; + +import java.util.concurrent.atomic.AtomicInteger; + +// Collection of functions for JavaScript-based interactions with a page. +public class JSUtils { + private static final int WAIT_TIMEOUT_SECONDS = 2; + private static final int CHECK_INTERVAL = 100; + + public static void clickOnLinkUsingJs( + final InstrumentationTestCase testCase, + final AwContents awContents, + final OnEvaluateJavaScriptResultHelper onEvaluateJavaScriptResultHelper, + final String linkId) throws Throwable { + + Assert.assertTrue(CriteriaHelper.pollForCriteria(new Criteria() { + @Override + public boolean isSatisfied() { + try { + String linkIsNotNull = executeJavaScriptAndWaitForResult(testCase, awContents, + onEvaluateJavaScriptResultHelper, + "document.getElementById('" + linkId + "') != null"); + return linkIsNotNull.equals("true"); + } catch (Throwable t) { + t.printStackTrace(); + Assert.fail("Failed to check if DOM is loaded: " + t.toString()); + return false; + } + } + }, WAIT_TIMEOUT_SECONDS * 1000, CHECK_INTERVAL)); + + testCase.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + awContents.getContentViewCore().evaluateJavaScript( + "var evObj = document.createEvent('Events'); " + + "evObj.initEvent('click', true, false); " + + "document.getElementById('" + linkId + "').dispatchEvent(evObj);" + + "console.log('element with id [" + linkId + "] clicked');"); + } + }); + } + + public static String executeJavaScriptAndWaitForResult( + InstrumentationTestCase testCase, + final AwContents awContents, + OnEvaluateJavaScriptResultHelper onEvaluateJavaScriptResultHelper, + final String code) throws Throwable { + final AtomicInteger requestId = new AtomicInteger(); + int currentCallCount = onEvaluateJavaScriptResultHelper.getCallCount(); + testCase.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + requestId.set(awContents.getContentViewCore().evaluateJavaScript(code)); + } + }); + onEvaluateJavaScriptResultHelper.waitForCallback(currentCallCount); + Assert.assertEquals("Response ID mismatch when evaluating JavaScript.", + requestId.get(), onEvaluateJavaScriptResultHelper.getId()); + return onEvaluateJavaScriptResultHelper.getJsonResult(); + } +} diff --git a/android_webview/lib/aw_browser_dependency_factory_impl.cc b/android_webview/lib/aw_browser_dependency_factory_impl.cc index 4eba90a..dccf033 100644 --- a/android_webview/lib/aw_browser_dependency_factory_impl.cc +++ b/android_webview/lib/aw_browser_dependency_factory_impl.cc @@ -6,6 +6,7 @@ // TODO(joth): Componentize or remove chrome/... dependencies. #include "android_webview/browser/net/aw_network_delegate.h" +#include "android_webview/browser/aw_request_interceptor.h" #include "android_webview/native/android_protocol_handler.h" #include "android_webview/native/aw_contents_container.h" #include "base/bind.h" @@ -59,10 +60,10 @@ void AwBrowserDependencyFactoryImpl::InstallInstance() { SetInstance(g_lazy_instance.Pointer()); } -// Initializing the Network Delegate here is only a temporary solution until we -// build an Android WebView specific BrowserContext that can handle building -// this internally. -void AwBrowserDependencyFactoryImpl::InitializeNetworkDelegateOnIOThread( +// Initializing the Network Delegate here is only a temporary solution until +// an Android WebView specific BrowserContext (which can then handle building +// this internally is) available. +void AwBrowserDependencyFactoryImpl::InitOnIOThreadWithBrowserContext( net::URLRequestContextGetter* normal_context, net::URLRequestContextGetter* incognito_context) { network_delegate_.reset(new AwNetworkDelegate()); @@ -70,6 +71,9 @@ void AwBrowserDependencyFactoryImpl::InitializeNetworkDelegateOnIOThread( network_delegate_.get()); incognito_context->GetURLRequestContext()->set_network_delegate( network_delegate_.get()); + + AwRequestInterceptor::RegisterInterceptorOnIOThread(normal_context); + AwRequestInterceptor::RegisterInterceptorOnIOThread(incognito_context); } void AwBrowserDependencyFactoryImpl::EnsureContextDependentHooksInitialized() @@ -81,7 +85,7 @@ void AwBrowserDependencyFactoryImpl::EnsureContextDependentHooksInitialized() profile->GetRequestContext()->GetNetworkTaskRunner()->PostTask( FROM_HERE, base::Bind( - &AwBrowserDependencyFactoryImpl::InitializeNetworkDelegateOnIOThread, + &AwBrowserDependencyFactoryImpl::InitOnIOThreadWithBrowserContext, base::Unretained(this), make_scoped_refptr(profile->GetRequestContext()), make_scoped_refptr( diff --git a/android_webview/lib/aw_browser_dependency_factory_impl.h b/android_webview/lib/aw_browser_dependency_factory_impl.h index 08eb1ef..f7cc270 100644 --- a/android_webview/lib/aw_browser_dependency_factory_impl.h +++ b/android_webview/lib/aw_browser_dependency_factory_impl.h @@ -33,7 +33,7 @@ class AwBrowserDependencyFactoryImpl : public AwBrowserDependencyFactory { content::WebContents* contents) OVERRIDE; private: - void InitializeNetworkDelegateOnIOThread( + void InitOnIOThreadWithBrowserContext( net::URLRequestContextGetter* normal_context, net::URLRequestContextGetter* incognito_context); void EnsureContextDependentHooksInitialized(); diff --git a/android_webview/native/android_webview_jni_registrar.cc b/android_webview/native/android_webview_jni_registrar.cc index bec2cb6..8fdff21 100644 --- a/android_webview/native/android_webview_jni_registrar.cc +++ b/android_webview/native/android_webview_jni_registrar.cc @@ -11,6 +11,7 @@ #include "android_webview/native/aw_contents_io_thread_client_impl.h" #include "android_webview/native/aw_http_auth_handler.h" #include "android_webview/native/cookie_manager.h" +#include "android_webview/native/intercepted_request_data_impl.h" #include "android_webview/native/js_result_handler.h" #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" @@ -33,6 +34,7 @@ static base::android::RegistrationMethod kWebViewRegisteredMethods[] = { { "AwContentsIoThreadClientImpl", RegisterAwContentsIoThreadClientImpl}, { "AwHttpAuthHandler", RegisterAwHttpAuthHandler }, { "CookieManager", RegisterCookieManager }, + { "InterceptedRequestDataImpl", RegisterInterceptedRequestData }, { "JsResultHandler", RegisterJsResultHandler }, }; diff --git a/android_webview/native/aw_contents_io_thread_client_impl.cc b/android_webview/native/aw_contents_io_thread_client_impl.cc index e798364..d302608 100644 --- a/android_webview/native/aw_contents_io_thread_client_impl.cc +++ b/android_webview/native/aw_contents_io_thread_client_impl.cc @@ -7,7 +7,7 @@ #include <map> #include <utility> -#include "android_webview/native/intercepted_request_data.h" +#include "android_webview/native/intercepted_request_data_impl.h" #include "base/android/jni_helper.h" #include "base/android/jni_string.h" #include "base/lazy_instance.h" @@ -15,10 +15,11 @@ #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_view_host.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" +#include "googleurl/src/gurl.h" #include "net/url_request/url_request.h" #include "jni/AwContentsIoThreadClient_jni.h" @@ -177,6 +178,7 @@ AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() { scoped_ptr<InterceptedRequestData> AwContentsIoThreadClientImpl::ShouldInterceptRequest( + const GURL& location, const net::URLRequest* request) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (java_object_.is_null()) @@ -184,14 +186,14 @@ AwContentsIoThreadClientImpl::ShouldInterceptRequest( JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> jstring_url = - ConvertUTF8ToJavaString(env, request->url().spec()); + ConvertUTF8ToJavaString(env, location.spec()); ScopedJavaLocalRef<jobject> ret = Java_AwContentsIoThreadClient_shouldInterceptRequest( env, java_object_.obj(), jstring_url.obj()); if (ret.is_null()) return scoped_ptr<InterceptedRequestData>(); return scoped_ptr<InterceptedRequestData>( - new InterceptedRequestData(ret)); + new InterceptedRequestDataImpl(ret)); } bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const { diff --git a/android_webview/native/aw_contents_io_thread_client_impl.h b/android_webview/native/aw_contents_io_thread_client_impl.h index 8a2a1d8..7e56127 100644 --- a/android_webview/native/aw_contents_io_thread_client_impl.h +++ b/android_webview/native/aw_contents_io_thread_client_impl.h @@ -10,7 +10,7 @@ #include "base/android/scoped_java_ref.h" #include "base/memory/scoped_ptr.h" -class InterceptedRequestData; +class GURL; namespace content { class WebContents; @@ -22,6 +22,8 @@ class URLRequest; namespace android_webview { +class InterceptedRequestData; + class AwContentsIoThreadClientImpl : public AwContentsIoThreadClient { public: // Associates the |jclient| instance (which must implement the @@ -35,6 +37,7 @@ class AwContentsIoThreadClientImpl : public AwContentsIoThreadClient { // Implementation of AwContentsIoThreadClient. virtual scoped_ptr<InterceptedRequestData> ShouldInterceptRequest( + const GURL& location, const net::URLRequest* request) OVERRIDE; virtual bool ShouldBlockContentUrls() const OVERRIDE; virtual bool ShouldBlockFileUrls() const OVERRIDE; diff --git a/android_webview/native/intercepted_request_data.cc b/android_webview/native/intercepted_request_data.cc deleted file mode 100644 index 4ef26a1..0000000 --- a/android_webview/native/intercepted_request_data.cc +++ /dev/null @@ -1,55 +0,0 @@ -// 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 "android_webview/native/intercepted_request_data.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "jni/InterceptedRequestData_jni.h" - -using std::string; -using base::android::ScopedJavaLocalRef; - -InterceptedRequestData::InterceptedRequestData( - const base::android::JavaRef<jobject>& obj) - : java_object_(obj) { -} - -InterceptedRequestData::~InterceptedRequestData() { -} - -ScopedJavaLocalRef<jobject> -InterceptedRequestData::GetInputStream(JNIEnv* env) const { - return Java_InterceptedRequestData_getData(env, java_object_.obj()); -} - -bool InterceptedRequestData::GetMimeType(JNIEnv* env, - string* mime_type) const { - ScopedJavaLocalRef<jstring> jstring_mime_type = - Java_InterceptedRequestData_getMimeType(env, java_object_.obj()); - if (jstring_mime_type.is_null()) - return false; - *mime_type = ConvertJavaStringToUTF8(jstring_mime_type); - return true; -} - -bool InterceptedRequestData::GetCharset( - JNIEnv* env, string* charset) const { - ScopedJavaLocalRef<jstring> jstring_charset = - Java_InterceptedRequestData_getCharset(env, java_object_.obj()); - if (jstring_charset.is_null()) - return false; - *charset = ConvertJavaStringToUTF8(jstring_charset); - return true; -} - -bool RegisterInterceptedRequestData(JNIEnv* env) { - if (g_InterceptedRequestData_clazz) - return true; - if (!base::android::HasClass(env, kInterceptedRequestDataClassPath)) { - DLOG(ERROR) << "Unable to find class InterceptedRequestData!"; - return false; - } - return RegisterNativesImpl(env); -} diff --git a/android_webview/native/intercepted_request_data.h b/android_webview/native/intercepted_request_data.h deleted file mode 100644 index acbe15d..0000000 --- a/android_webview/native/intercepted_request_data.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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 CONTENT_BROWSER_ANDROID_INTERCEPTED_REQUEST_DATA_H_ -#define CONTENT_BROWSER_ANDROID_INTERCEPTED_REQUEST_DATA_H_ - -#include <string> - -#include "base/android/scoped_java_ref.h" -#include "base/memory/ref_counted.h" - -// This class represents the Java-side data that is to be used to complete a -// particular URLRequest. -class InterceptedRequestData { - public: - InterceptedRequestData(const base::android::JavaRef<jobject>& obj); - ~InterceptedRequestData(); - - base::android::ScopedJavaLocalRef<jobject> GetInputStream(JNIEnv* env) const; - bool GetMimeType(JNIEnv* env, std::string* mime_type) const; - bool GetCharset(JNIEnv* env, std::string* charset) const; - - private: - base::android::ScopedJavaGlobalRef<jobject> java_object_; - - DISALLOW_COPY_AND_ASSIGN(InterceptedRequestData); -}; - -bool RegisterInterceptedRequestData(JNIEnv* env); - -#endif // CONTENT_BROWSER_ANDROID_INTERCEPTED_REQUEST_DATA_H_ diff --git a/android_webview/native/intercepted_request_data_impl.cc b/android_webview/native/intercepted_request_data_impl.cc new file mode 100644 index 0000000..e3d9096 --- /dev/null +++ b/android_webview/native/intercepted_request_data_impl.cc @@ -0,0 +1,101 @@ +// 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 "android_webview/native/intercepted_request_data_impl.h" + +#include "android_webview/native/android_stream_reader_url_request_job.h" +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "jni/InterceptedRequestData_jni.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_job.h" + +using base::android::ScopedJavaLocalRef; + +namespace android_webview { + +namespace { + +class StreamReaderJobDelegateImpl : + public AndroidStreamReaderURLRequestJob::Delegate { + public: + StreamReaderJobDelegateImpl( + const InterceptedRequestDataImpl* intercepted_request_data) + : intercepted_request_data_impl_(intercepted_request_data) { + DCHECK(intercepted_request_data_impl_); + } + + virtual base::android::ScopedJavaLocalRef<jobject> OpenInputStream( + JNIEnv* env, + net::URLRequest* request) OVERRIDE { + return intercepted_request_data_impl_->GetInputStream(env); + } + + virtual bool GetMimeType(JNIEnv* env, + net::URLRequest* request, + jobject stream, + std::string* mime_type) OVERRIDE { + return intercepted_request_data_impl_->GetMimeType(env, mime_type); + } + + virtual bool GetCharset(JNIEnv* env, + net::URLRequest* request, + jobject stream, + std::string* charset) OVERRIDE { + return intercepted_request_data_impl_->GetCharset(env, charset); + } + + private: + const InterceptedRequestDataImpl* intercepted_request_data_impl_; +}; + +} // namespace + +InterceptedRequestDataImpl::InterceptedRequestDataImpl( + const base::android::JavaRef<jobject>& obj) + : java_object_(obj) { +} + +InterceptedRequestDataImpl::~InterceptedRequestDataImpl() { +} + +ScopedJavaLocalRef<jobject> +InterceptedRequestDataImpl::GetInputStream(JNIEnv* env) const { + return Java_InterceptedRequestData_getData(env, java_object_.obj()); +} + +bool InterceptedRequestDataImpl::GetMimeType(JNIEnv* env, + std::string* mime_type) const { + ScopedJavaLocalRef<jstring> jstring_mime_type = + Java_InterceptedRequestData_getMimeType(env, java_object_.obj()); + if (jstring_mime_type.is_null()) + return false; + *mime_type = ConvertJavaStringToUTF8(jstring_mime_type); + return true; +} + +bool InterceptedRequestDataImpl::GetCharset( + JNIEnv* env, std::string* charset) const { + ScopedJavaLocalRef<jstring> jstring_charset = + Java_InterceptedRequestData_getCharset(env, java_object_.obj()); + if (jstring_charset.is_null()) + return false; + *charset = ConvertJavaStringToUTF8(jstring_charset); + return true; +} + +bool RegisterInterceptedRequestData(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +net::URLRequestJob* InterceptedRequestDataImpl::CreateJobFor( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const { + scoped_ptr<AndroidStreamReaderURLRequestJob::Delegate> + stream_reader_job_delegate_impl(new StreamReaderJobDelegateImpl(this)); + return new AndroidStreamReaderURLRequestJob( + request, network_delegate, stream_reader_job_delegate_impl.Pass()); +} + +} // namespace android_webview diff --git a/android_webview/native/intercepted_request_data_impl.h b/android_webview/native/intercepted_request_data_impl.h new file mode 100644 index 0000000..ecafaa7 --- /dev/null +++ b/android_webview/native/intercepted_request_data_impl.h @@ -0,0 +1,40 @@ +// 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 ANDROID_WEBVIEW_NATIVE_INTERCEPTED_REQUEST_DATA_IMPL_H_ +#define ANDROID_WEBVIEW_NATIVE_INTERCEPTED_REQUEST_DATA_IMPL_H_ + +#include "android_webview/browser/intercepted_request_data.h" +#include "base/android/scoped_java_ref.h" +#include "base/compiler_specific.h" + +namespace android_webview { + +class InterceptedRequestDataImpl : public InterceptedRequestData { + public: + // It is expected that |obj| is an instance of the Java-side + // org.chromium.android_webview.InterceptedRequestData class. + InterceptedRequestDataImpl(const base::android::JavaRef<jobject>& obj); + virtual ~InterceptedRequestDataImpl(); + + virtual base::android::ScopedJavaLocalRef<jobject> + GetInputStream(JNIEnv* env) const; + virtual bool GetMimeType(JNIEnv* env, std::string* mime_type) const; + virtual bool GetCharset(JNIEnv* env, std::string* charset) const; + + virtual net::URLRequestJob* CreateJobFor( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const OVERRIDE; + + private: + base::android::ScopedJavaGlobalRef<jobject> java_object_; + + DISALLOW_COPY_AND_ASSIGN(InterceptedRequestDataImpl); +}; + +bool RegisterInterceptedRequestData(JNIEnv* env); + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_NATIVE_INTERCEPTED_REQUEST_DATA_IMPL_H_ diff --git a/android_webview/native/webview_native.gyp b/android_webview/native/webview_native.gyp index 95f0cde..af790f0 100644 --- a/android_webview/native/webview_native.gyp +++ b/android_webview/native/webview_native.gyp @@ -43,8 +43,8 @@ 'aw_web_contents_delegate.h', 'cookie_manager.cc', 'cookie_manager.h', - 'intercepted_request_data.cc', - 'intercepted_request_data.h', + 'intercepted_request_data_impl.cc', + 'intercepted_request_data_impl.h', 'js_result_handler.cc', 'js_result_handler.h', ], |