diff options
21 files changed, 293 insertions, 243 deletions
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc index 5c3aaf9..96ed105 100644 --- a/android_webview/browser/aw_content_browser_client.cc +++ b/android_webview/browser/aw_content_browser_client.cc @@ -33,6 +33,7 @@ #include "components/navigation_interception/intercept_navigation_delegate.h" #include "content/public/browser/access_token_store.h" #include "content/public/browser/browser_message_filter.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/navigation_handle.h" @@ -51,6 +52,7 @@ #include "ui/base/resource/resource_bundle_android.h" #include "ui/resources/grit/ui_resources.h" +using content::BrowserThread; using content::ResourceType; namespace android_webview { @@ -64,8 +66,16 @@ public: explicit AwContentsMessageFilter(int process_id); // BrowserMessageFilter methods. + void OverrideThreadForMessage(const IPC::Message& message, + BrowserThread::ID* thread) override; bool OnMessageReceived(const IPC::Message& message) override; + void OnShouldOverrideUrlLoading(int routing_id, + const base::string16& url, + bool has_user_gesture, + bool is_redirect, + bool is_main_frame, + bool* ignore_navigation); void OnSubFrameCreated(int parent_render_frame_id, int child_render_frame_id); private: @@ -84,15 +94,45 @@ AwContentsMessageFilter::AwContentsMessageFilter(int process_id) AwContentsMessageFilter::~AwContentsMessageFilter() { } +void AwContentsMessageFilter::OverrideThreadForMessage( + const IPC::Message& message, + BrowserThread::ID* thread) { + if (message.type() == AwViewHostMsg_ShouldOverrideUrlLoading::ID) { + *thread = BrowserThread::UI; + } +} + bool AwContentsMessageFilter::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(AwContentsMessageFilter, message) + IPC_MESSAGE_HANDLER(AwViewHostMsg_ShouldOverrideUrlLoading, + OnShouldOverrideUrlLoading) IPC_MESSAGE_HANDLER(AwViewHostMsg_SubFrameCreated, OnSubFrameCreated) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } +void AwContentsMessageFilter::OnShouldOverrideUrlLoading( + int render_frame_id, + const base::string16& url, + bool has_user_gesture, + bool is_redirect, + bool is_main_frame, + bool* ignore_navigation) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + *ignore_navigation = false; + AwContentsClientBridgeBase* client = + AwContentsClientBridgeBase::FromID(process_id_, render_frame_id); + if (client) { + *ignore_navigation = client->ShouldOverrideUrlLoading( + url, has_user_gesture, is_redirect, is_main_frame); + } else { + LOG(WARNING) << "Failed to find the associated render view host for url: " + << url; + } +} + void AwContentsMessageFilter::OnSubFrameCreated(int parent_render_frame_id, int child_render_frame_id) { AwContentsIoThreadClient::SubFrameCreated( @@ -520,10 +560,10 @@ ScopedVector<content::NavigationThrottle> AwContentBrowserClient::CreateThrottlesForNavigation( content::NavigationHandle* navigation_handle) { ScopedVector<content::NavigationThrottle> throttles; - if (navigation_handle->IsInMainFrame() || - (!navigation_handle->GetURL().SchemeIs(url::kHttpScheme) && - !navigation_handle->GetURL().SchemeIs(url::kHttpsScheme) && - !navigation_handle->GetURL().SchemeIs(url::kAboutScheme))) { + // We allow intercepting only navigations within main frames. This + // is used to post onPageStarted. We handle shouldOverrideUrlLoading + // via a sync IPC. + if (navigation_handle->IsInMainFrame()) { throttles.push_back( navigation_interception::InterceptNavigationDelegate::CreateThrottleFor( navigation_handle)); diff --git a/android_webview/browser/aw_contents_client_bridge_base.cc b/android_webview/browser/aw_contents_client_bridge_base.cc index d86e76a..d684d40 100644 --- a/android_webview/browser/aw_contents_client_bridge_base.cc +++ b/android_webview/browser/aw_contents_client_bridge_base.cc @@ -53,6 +53,18 @@ AwContentsClientBridgeBase* AwContentsClientBridgeBase::FromWebContents( return UserData::GetContents(web_contents); } +// static +AwContentsClientBridgeBase* AwContentsClientBridgeBase::FromID( + int render_process_id, + int render_frame_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + content::RenderFrameHost* rfh = + content::RenderFrameHost::FromID(render_process_id, render_frame_id); + content::WebContents* web_contents = + content::WebContents::FromRenderFrameHost(rfh); + return UserData::GetContents(web_contents); +} + AwContentsClientBridgeBase::~AwContentsClientBridgeBase() { } diff --git a/android_webview/browser/aw_contents_client_bridge_base.h b/android_webview/browser/aw_contents_client_bridge_base.h index c03cb36..6ac592b 100644 --- a/android_webview/browser/aw_contents_client_bridge_base.h +++ b/android_webview/browser/aw_contents_client_bridge_base.h @@ -35,6 +35,8 @@ class AwContentsClientBridgeBase { AwContentsClientBridgeBase* handler); static AwContentsClientBridgeBase* FromWebContents( content::WebContents* web_contents); + static AwContentsClientBridgeBase* FromID(int render_process_id, + int render_frame_id); virtual ~AwContentsClientBridgeBase(); @@ -60,6 +62,11 @@ class AwContentsClientBridgeBase { const base::string16& message_text, const content::JavaScriptDialogManager::DialogClosedCallback& callback) = 0; + + virtual bool ShouldOverrideUrlLoading(const base::string16& url, + bool has_user_gesture, + bool is_redirect, + bool is_main_frame) = 0; }; } // namespace android_webview diff --git a/android_webview/common/render_view_messages.h b/android_webview/common/render_view_messages.h index 4c42f3d..24df3cd 100644 --- a/android_webview/common/render_view_messages.h +++ b/android_webview/common/render_view_messages.h @@ -100,6 +100,20 @@ IPC_MESSAGE_ROUTED1(AwViewHostMsg_UpdateHitTestData, IPC_MESSAGE_ROUTED1(AwViewHostMsg_OnContentsSizeChanged, gfx::Size /* contents_size */) +// Sent immediately before a top level navigation is initiated within Blink. +// There are some exlusions, the most important ones are it is not sent +// when creating a popup window, and not sent for application initiated +// navigations. See AwContentRendererClient::HandleNavigation for all +// cornercases. This is sent before updating the NavigationController state +// or creating a URLRequest for the main frame resource. +IPC_SYNC_MESSAGE_CONTROL5_1(AwViewHostMsg_ShouldOverrideUrlLoading, + int /* render_frame_id id */, + base::string16 /* in - url */, + bool /* in - has_user_gesture */, + bool /* in - is_redirect */, + bool /* in - is_main_frame */, + bool /* out - result */) + // Sent when a subframe is created. IPC_MESSAGE_CONTROL2(AwViewHostMsg_SubFrameCreated, int /* parent_render_frame_id */, 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 2352b57..c2ce4c9 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -291,6 +291,10 @@ public class AwContents implements SmartClipProvider, private PostMessageSender mPostMessageSender; + // This flag indicates that ShouldOverrideUrlNavigation should be posted + // through the resourcethrottle. This is only used for popup windows. + private boolean mDeferredShouldOverrideUrlLoadingIsPendingForPopup; + // This is a workaround for some qualcomm devices discarding buffer on // Activity restore. private boolean mInvalidateRootViewOnNextDraw; @@ -500,38 +504,23 @@ public class AwContents implements SmartClipProvider, @Override public boolean shouldIgnoreNavigation(NavigationParams navigationParams) { final String url = navigationParams.url; - - final int transitionType = navigationParams.pageTransitionType; - final boolean isLoadUrl = (transitionType & PageTransition.FROM_API) != 0; - final boolean isBackForward = (transitionType & PageTransition.FORWARD_BACK) != 0; - final boolean isReload = - (transitionType & PageTransition.CORE_MASK) == PageTransition.RELOAD; - final boolean isRedirect = navigationParams.isRedirect; - boolean ignoreNavigation = false; - // Any navigation from loadUrl, goBack/Forward, or reload, are considered application - // initiated and hence will not yield a shouldOverrideUrlLoading() callback. - if ((!isLoadUrl || isRedirect) && !isBackForward && !isReload - && !navigationParams.isPost) { - ignoreNavigation = mContentsClient.shouldIgnoreNavigation(mContext, url, - navigationParams.isMainFrame, - navigationParams.hasUserGesture || navigationParams.hasUserGestureCarryover, - navigationParams.isRedirect); + if (mDeferredShouldOverrideUrlLoadingIsPendingForPopup) { + mDeferredShouldOverrideUrlLoadingIsPendingForPopup = false; + // If this is used for all navigations in future, cases for application initiated + // load, redirect and backforward should also be filtered out. + if (!navigationParams.isPost) { + ignoreNavigation = mContentsClient.shouldIgnoreNavigation( + mContext, url, navigationParams.isMainFrame, + navigationParams.hasUserGesture + || navigationParams.hasUserGestureCarryover, + navigationParams.isRedirect); + } } - // The shouldOverrideUrlLoading call might have resulted in posting messages to the // UI thread. Using sendMessage here (instead of calling onPageStarted directly) // will allow those to run in order. - if (isRedirect) { - mContentsClient.getCallbackHelper().postOnPageStarted(url); - // We can post onPageFinished here since we know that the navigation will fail. - // Also AwWebContentsObserver.didFail does not call OnPageFinished when the - // navigation is overridden because we don't want an onPageFinished for such a - // navigation unless it is a redirect. - if (ignoreNavigation) { - mContentsClient.getCallbackHelper().postOnPageFinished(url); - } - } else if (!ignoreNavigation) { + if (!ignoreNavigation) { mContentsClient.getCallbackHelper().postOnPageStarted(url); } return ignoreNavigation; @@ -1030,6 +1019,7 @@ public class AwContents implements SmartClipProvider, // Recap: supplyContentsForPopup() is called on the parent window's content, this method is // called on the popup window's content. private void receivePopupContents(long popupNativeAwContents) { + mDeferredShouldOverrideUrlLoadingIsPendingForPopup = true; // Save existing view state. final boolean wasAttached = mIsAttachedToWindow; final boolean wasViewVisible = mIsViewVisible; 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 ba9385f..47d0f28 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java @@ -334,8 +334,6 @@ public abstract class AwContentsClient { public abstract void onPageCommitVisible(String url); - public void onFailedLoadForTesting(String url) {} - public final void onReceivedError(AwWebResourceRequest request, AwWebResourceError error) { // Only one of these callbacks actually reaches out the client code. The first callback // is used on API versions up to and including L, the second on subsequent releases. diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java index efca3d0..8c29c2d 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java @@ -250,6 +250,13 @@ public class AwContentsClientBridge { mClient.handleJsBeforeUnload(url, message, handler); } + @CalledByNative + private boolean shouldOverrideUrlLoading( + String url, boolean hasUserGesture, boolean isRedirect, boolean isMainFrame) { + return mClient.shouldIgnoreNavigation( + mContext, url, isMainFrame, hasUserGesture, isRedirect); + } + void confirmJsResult(int id, String prompt) { if (mNativeContentsClientBridge == 0) return; nativeConfirmJsResult(mNativeContentsClientBridge, id, prompt); diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java index d9b709a..3c08c30 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java +++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java @@ -72,16 +72,11 @@ public class AwWebContentsObserver extends WebContentsObserver { String unreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl(); boolean isErrorUrl = unreachableWebDataUrl != null && unreachableWebDataUrl.equals(failingUrl); - if (isMainFrame && !isErrorUrl && errorCode == NetError.ERR_ABORTED - && !wasIgnoredByHandler) { + if (isMainFrame && !isErrorUrl && errorCode == NetError.ERR_ABORTED) { // Need to call onPageFinished for backwards compatibility with the classic webview. // See also AwContents.IoThreadClientImpl.onReceivedError. - // If the navigation was ignored because of shouldOverrideUrlLoading we have already - // called onPageFinished in - // AwContents.InterceptNavigationDelegateImpl.shouldIgnoreNavigation instead. client.getCallbackHelper().postOnPageFinished(failingUrl); } - client.onFailedLoadForTesting(failingUrl); } @Override diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java index 0694501..e27672f 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldOverrideUrlLoadingTest.java @@ -9,10 +9,8 @@ import android.util.Pair; import org.chromium.android_webview.AwContents; import org.chromium.android_webview.AwContentsClient; -import org.chromium.android_webview.test.TestAwContentsClient.OnFailedLoadHelper; import org.chromium.android_webview.test.util.CommonResources; import org.chromium.android_webview.test.util.JSUtils; -import org.chromium.android_webview.test.util.JavascriptEventObserver; import org.chromium.base.annotations.SuppressFBWarnings; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.DisabledTest; @@ -20,7 +18,6 @@ import org.chromium.base.test.util.Feature; import org.chromium.content.browser.test.util.CallbackHelper; import org.chromium.content.browser.test.util.DOMUtils; import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper; -import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper; import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageStartedHelper; import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnReceivedErrorHelper; import org.chromium.content.common.ContentSwitches; @@ -30,7 +27,6 @@ import org.chromium.net.test.util.TestWebServer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** @@ -736,9 +732,7 @@ public class AwContentsClientShouldOverrideUrlLoadingTest extends AwTestBase { assertEquals(redirectTarget, mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl()); assertEquals(serverSideRedirect, mShouldOverrideUrlLoadingHelper.isRedirect()); - // We keep the user gesture from the initial navigation for serverside redirects but drop - // the user gesture for browser initiated redirects. - assertEquals(serverSideRedirect, mShouldOverrideUrlLoadingHelper.hasUserGesture()); + assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture()); assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame()); } @@ -1086,164 +1080,4 @@ public class AwContentsClientShouldOverrideUrlLoadingTest extends AwTestBase { public void testClickableAddressInIframe() throws Throwable { doTestClickableContent(TEST_ADDRESS, TEST_ADDRESS_URI, false); } - - @SmallTest - @Feature({"AndroidWebView"}) - public void testXhrInLink() throws Throwable { - standardSetup(); - final CountDownLatch shouldOverrideUrlLoadingSignal = new CountDownLatch(1); - - final String xhrPath = "/xhrPath.html"; - final String xhrUrl = mWebServer.setResponseWithRunnableAction( - xhrPath, CommonResources.makeHtmlPageFrom("", ""), null, new Runnable() { - @Override - public void run() { - try { - shouldOverrideUrlLoadingSignal.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - }); - - final String xhrJs = "function xhrFunction() {" - + " var xhr = new XMLHttpRequest();" - + " xhr.onload=function() {" - + " console.info('xhr loaded');" - + " window.jsInterface.setValue(true);" - + " };" - + " xhr.onerror=function() {" - + " console.info('xhr failed, status ' + xhr.status);" - + " window.jsInterface.setValue(false);" - + " };" - + " xhr.open('GET', '" + xhrUrl + "', true);" - + " xhr.send();" - + "};"; - - String pageWithXhrLink = makeHtmlPageFrom( - "<script>" + xhrJs + "</script>", "<img onclick=\"xhrFunction(); location.href='" - + "thiswillbe://intercepted/" - + "'\" class=\"big\" id=\"link\" />"); - - final String startPath = "/startPath.html"; - final String startUrl = addPageToTestServer(startPath, pageWithXhrLink); - - enableJavaScriptOnUiThread(mAwContents); - final BooleanValueJavascriptObserver jsInterface = new BooleanValueJavascriptObserver(); - - // add javascript interface - getInstrumentation().runOnMainSync(new Runnable() { - @Override - public void run() { - jsInterface.register(mAwContents.getContentViewCore(), "jsInterface"); - } - }); - - loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), startUrl); - - setShouldOverrideUrlLoadingReturnValueOnUiThread(true); - final int shouldOverrideUrlLoadingCallCount = - mShouldOverrideUrlLoadingHelper.getCallCount(); - - assertEquals(0, mWebServer.getRequestCount(xhrPath)); - - clickOnLinkUsingJs(); - - // Make the server xhr response wait until the navigation request is intercepted. - mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount); - shouldOverrideUrlLoadingSignal.countDown(); - - jsInterface.waitForEvent(WAIT_TIMEOUT_MS); - assertTrue(jsInterface.getValue()); - assertEquals(1, mWebServer.getRequestCount(xhrPath)); - } - - private static class BooleanValueJavascriptObserver extends JavascriptEventObserver { - private boolean mValue = false; - - public void setValue(boolean value) { - mValue = value; - notifyJava(); - } - - public boolean getValue() { - return mValue; - } - } - - /** - * This is to test a bug where a JS redirect failing in its provisional state would prevent us - * from posting onPageFinished for the original page load. - * The original page contains an iframe so that we can commit the original load but then - * prevent it from finishing until the JS redirect fails by having the test server defer the - * response to the iframe. - */ - @SmallTest - @Feature({"AndroidWebView"}) - public void testOnPageFinishedOnFailedJSRedirect() throws Throwable { - final CountDownLatch jsRedirectSignal = new CountDownLatch(1); - - final String redirectTargetPath = "/redirectTargetPath.html"; - final String redirectTargetUrl = mWebServer.setResponse( - redirectTargetPath, CommonResources.makeHtmlPageFrom("", ""), null); - - class DelayingOverrideClient extends TestAwContentsClient { - @Override - public boolean shouldOverrideUrlLoading(AwWebResourceRequest request) { - if (redirectTargetUrl.equals(request.url)) { - try { - // Wait here to make sure the load reaches its provisional state before we - // cancel it. Waiting for a callback to the WebContentsObserver to make sure - // we have reached the provisional state causes a deadlock here. - Thread.sleep(Math.min(WAIT_TIMEOUT_MS / 2, 2000)); - } catch (InterruptedException e) { - } - return true; - } - return false; - } - } - mContentsClient = new DelayingOverrideClient(); - setupWithProvidedContentsClient(mContentsClient); - - final String redirectJs = "window.location.href='" + redirectTargetUrl + "';"; - - final String iframePath = "/iframePath.html"; - final String iframeUrl = mWebServer.setResponseWithRunnableAction( - iframePath, CommonResources.makeHtmlPageFrom("", ""), null, new Runnable() { - @Override - public void run() { - try { - mAwContents.evaluateJavaScriptForTests(redirectJs, null); - jsRedirectSignal.await(); - } catch (InterruptedException e) { - } - } - }); - final String iframeJs = "<iframe src='" + iframeUrl + "'></iframe>"; - - String startPage = makeHtmlPageFrom("", iframeJs); - final String startPath = "/startPath.html"; - final String startUrl = addPageToTestServer(startPath, startPage); - - enableJavaScriptOnUiThread(mAwContents); - - OnPageFinishedHelper onPageFinishedHelper = mContentsClient.getOnPageFinishedHelper(); - int onPageFinishedCallCount = onPageFinishedHelper.getCallCount(); - - OnFailedLoadHelper onFailedLoadHelper = mContentsClient.getOnFailedLoadHelper(); - int onFailedLoadCallCount = onFailedLoadHelper.getCallCount(); - - // load start url -> iframe -> JS redirect -> fail JS redirect -> finish start URL - loadUrlAsync(mAwContents, startUrl); - - onFailedLoadHelper.waitForCallback(onFailedLoadCallCount); - assertEquals(redirectTargetUrl, onFailedLoadHelper.getUrl()); - - // let iframe finish - jsRedirectSignal.countDown(); - - onPageFinishedHelper.waitForCallback(onPageFinishedCallCount); - assertEquals(startUrl, onPageFinishedHelper.getUrl()); - } } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java index 428e97c..084b446 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/TestAwContentsClient.java @@ -31,7 +31,6 @@ public class TestAwContentsClient extends NullContentsClient { private boolean mAllowSslError; private final OnPageStartedHelper mOnPageStartedHelper; private final OnPageFinishedHelper mOnPageFinishedHelper; - private final OnFailedLoadHelper mOnFailedLoadHelper; private final OnPageCommitVisibleHelper mOnPageCommitVisibleHelper; private final OnReceivedErrorHelper mOnReceivedErrorHelper; private final OnReceivedError2Helper mOnReceivedError2Helper; @@ -54,7 +53,6 @@ public class TestAwContentsClient extends NullContentsClient { super(ThreadUtils.getUiThreadLooper()); mOnPageStartedHelper = new OnPageStartedHelper(); mOnPageFinishedHelper = new OnPageFinishedHelper(); - mOnFailedLoadHelper = new OnFailedLoadHelper(); mOnPageCommitVisibleHelper = new OnPageCommitVisibleHelper(); mOnReceivedErrorHelper = new OnReceivedErrorHelper(); mOnReceivedError2Helper = new OnReceivedError2Helper(); @@ -87,25 +85,6 @@ public class TestAwContentsClient extends NullContentsClient { return mOnPageFinishedHelper; } - /** - * CallbackHelper for OnFailedLoad. - */ - public static class OnFailedLoadHelper extends CallbackHelper { - private String mUrl; - public void notifyCalled(String url) { - mUrl = url; - notifyCalled(); - } - public String getUrl() { - assert getCallCount() > 0; - return mUrl; - } - } - - public OnFailedLoadHelper getOnFailedLoadHelper() { - return mOnFailedLoadHelper; - } - public OnReceivedErrorHelper getOnReceivedErrorHelper() { return mOnReceivedErrorHelper; } @@ -236,11 +215,6 @@ public class TestAwContentsClient extends NullContentsClient { } @Override - public void onFailedLoadForTesting(String url) { - mOnFailedLoadHelper.notifyCalled(url); - } - - @Override public void onReceivedError(int errorCode, String description, String failingUrl) { mOnReceivedErrorHelper.notifyCalled(errorCode, description, failingUrl); } diff --git a/android_webview/native/aw_contents_client_bridge.cc b/android_webview/native/aw_contents_client_bridge.cc index 02db65b..4d2fd4dcb 100644 --- a/android_webview/native/aw_contents_client_bridge.cc +++ b/android_webview/native/aw_contents_client_bridge.cc @@ -341,6 +341,21 @@ void AwContentsClientBridge::RunBeforeUnloadDialog( env, obj.obj(), jurl.obj(), jmessage.obj(), callback_id); } +bool AwContentsClientBridge::ShouldOverrideUrlLoading(const base::string16& url, + bool has_user_gesture, + bool is_redirect, + bool is_main_frame) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return false; + ScopedJavaLocalRef<jstring> jurl = ConvertUTF16ToJavaString(env, url); + devtools_instrumentation::ScopedEmbedderCallbackTask( + "shouldOverrideUrlLoading"); + return Java_AwContentsClientBridge_shouldOverrideUrlLoading( + env, obj.obj(), jurl.obj(), has_user_gesture, is_redirect, is_main_frame); +} + void AwContentsClientBridge::ConfirmJsResult(JNIEnv* env, const JavaRef<jobject>&, int id, diff --git a/android_webview/native/aw_contents_client_bridge.h b/android_webview/native/aw_contents_client_bridge.h index 5372fb0..2af63ca 100644 --- a/android_webview/native/aw_contents_client_bridge.h +++ b/android_webview/native/aw_contents_client_bridge.h @@ -54,6 +54,10 @@ class AwContentsClientBridge : public AwContentsClientBridgeBase { const base::string16& message_text, const content::JavaScriptDialogManager::DialogClosedCallback& callback) override; + bool ShouldOverrideUrlLoading(const base::string16& url, + bool has_user_gesture, + bool is_redirect, + bool is_main_frame) override; // Methods called from Java. void ProceedSslError(JNIEnv* env, diff --git a/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc index f25b936..7203f90 100644 --- a/android_webview/renderer/aw_content_renderer_client.cc +++ b/android_webview/renderer/aw_content_renderer_client.cc @@ -29,6 +29,8 @@ #include "components/printing/renderer/print_web_view_helper.h" #include "components/visitedlink/renderer/visitedlink_slave.h" #include "content/public/common/url_constants.h" +#include "content/public/renderer/document_state.h" +#include "content/public/renderer/navigation_state.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" @@ -39,6 +41,7 @@ #include "third_party/WebKit/public/platform/WebURLError.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebNavigationType.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -76,6 +79,61 @@ void AwContentRendererClient::RenderThreadStarted() { blink::WebSecurityPolicy::registerURLSchemeAsSecure(aw_scheme); } +bool AwContentRendererClient::HandleNavigation( + content::RenderFrame* render_frame, + bool is_content_initiated, + int opener_id, + blink::WebFrame* frame, + const blink::WebURLRequest& request, + blink::WebNavigationType type, + blink::WebNavigationPolicy default_policy, + bool is_redirect) { + // Only GETs can be overridden. + if (!request.httpMethod().equals("GET")) + return false; + + // Any navigation from loadUrl, and goBack/Forward are considered application- + // initiated and hence will not yield a shouldOverrideUrlLoading() callback. + // Webview classic does not consider reload application-initiated so we + // continue the same behavior. + // TODO(sgurun) is_content_initiated is normally false for cross-origin + // navigations but since android_webview does not swap out renderers, this + // works fine. This will stop working if android_webview starts swapping out + // renderers on navigation. + bool application_initiated = + !is_content_initiated || type == blink::WebNavigationTypeBackForward; + + // Don't offer application-initiated navigations unless it's a redirect. + if (application_initiated && !is_redirect) + return false; + + bool is_main_frame = !frame->parent(); + const GURL& gurl = request.url(); + // For HTTP schemes, only top-level navigations can be overridden. Similarly, + // WebView Classic lets app override only top level about:blank navigations. + // So we filter out non-top about:blank navigations here. + if (!is_main_frame && + (gurl.SchemeIs(url::kHttpScheme) || gurl.SchemeIs(url::kHttpsScheme) || + gurl.SchemeIs(url::kAboutScheme))) + return false; + + // use NavigationInterception throttle to handle the call as that can + // be deferred until after the java side has been constructed. + if (opener_id != MSG_ROUTING_NONE) { + return false; + } + + bool ignore_navigation = false; + base::string16 url = request.url().string(); + bool has_user_gesture = request.hasUserGesture(); + + int render_frame_id = render_frame->GetRoutingID(); + RenderThread::Get()->Send(new AwViewHostMsg_ShouldOverrideUrlLoading( + render_frame_id, url, has_user_gesture, is_redirect, is_main_frame, + &ignore_navigation)); + return ignore_navigation; +} + void AwContentRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { new AwContentSettingsClient(render_frame); diff --git a/android_webview/renderer/aw_content_renderer_client.h b/android_webview/renderer/aw_content_renderer_client.h index 8f05468..a1717de 100644 --- a/android_webview/renderer/aw_content_renderer_client.h +++ b/android_webview/renderer/aw_content_renderer_client.h @@ -38,6 +38,14 @@ class AwContentRendererClient : public content::ContentRendererClient { bool IsLinkVisited(unsigned long long link_hash) override; void AddKeySystems(std::vector<media::KeySystemInfo>* key_systems) override; + bool HandleNavigation(content::RenderFrame* render_frame, + bool is_content_initiated, + int opener_id, + blink::WebFrame* frame, + const blink::WebURLRequest& request, + blink::WebNavigationType type, + blink::WebNavigationPolicy default_policy, + bool is_redirect) override; bool ShouldOverridePageVisibilityState( const content::RenderFrame* render_frame, blink::WebPageVisibilityState* override_state) override; diff --git a/components/navigation_interception/intercept_navigation_throttle.cc b/components/navigation_interception/intercept_navigation_throttle.cc index a58bce9..4ad87b2 100644 --- a/components/navigation_interception/intercept_navigation_throttle.cc +++ b/components/navigation_interception/intercept_navigation_throttle.cc @@ -65,8 +65,7 @@ InterceptNavigationThrottle::CheckIfShouldIgnoreNavigation(bool is_redirect) { navigation_handle()->GetURL(), navigation_handle()->GetReferrer(), navigation_handle()->HasUserGesture(), navigation_handle()->IsPost(), navigation_handle()->GetPageTransition(), is_redirect, - navigation_handle()->IsExternalProtocol(), - navigation_handle()->IsInMainFrame()); + navigation_handle()->IsExternalProtocol(), true); if (run_callback_synchronously_) { bool should_ignore_navigation = should_ignore_callback_.Run( diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc index a0a9c40..532cf01 100644 --- a/content/browser/web_contents/web_contents_impl_browsertest.cc +++ b/content/browser/web_contents/web_contents_impl_browsertest.cc @@ -189,6 +189,60 @@ class LoadingStateChangedDelegate : public WebContentsDelegate { int loadingStateToDifferentDocumentCount_; }; +// See: http://crbug.com/298193 +#if defined(OS_WIN) || defined(OS_LINUX) +#define MAYBE_DidStopLoadingDetails DISABLED_DidStopLoadingDetails +#else +#define MAYBE_DidStopLoadingDetails DidStopLoadingDetails +#endif + +// Test that DidStopLoading includes the correct URL in the details. +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, + MAYBE_DidStopLoadingDetails) { + ASSERT_TRUE(embedded_test_server()->Start()); + + LoadStopNotificationObserver load_observer( + &shell()->web_contents()->GetController()); + NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")); + load_observer.Wait(); + + EXPECT_EQ("/title1.html", load_observer.url_.path()); + EXPECT_EQ(0, load_observer.session_index_); + EXPECT_EQ(&shell()->web_contents()->GetController(), + load_observer.controller_); +} + +// See: http://crbug.com/298193 +#if defined(OS_WIN) || defined(OS_LINUX) +#define MAYBE_DidStopLoadingDetailsWithPending \ + DISABLED_DidStopLoadingDetailsWithPending +#else +#define MAYBE_DidStopLoadingDetailsWithPending DidStopLoadingDetailsWithPending +#endif + +// Test that DidStopLoading includes the correct URL in the details when a +// pending entry is present. +IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, + MAYBE_DidStopLoadingDetailsWithPending) { + ASSERT_TRUE(embedded_test_server()->Start()); + GURL url("data:text/html,<div>test</div>"); + + // Listen for the first load to stop. + LoadStopNotificationObserver load_observer( + &shell()->web_contents()->GetController()); + // Start a new pending navigation as soon as the first load commits. + // We will hear a DidStopLoading from the first load as the new load + // is started. + NavigateOnCommitObserver commit_observer( + shell(), embedded_test_server()->GetURL("/title2.html")); + NavigateToURL(shell(), url); + load_observer.Wait(); + + EXPECT_EQ(url, load_observer.url_); + EXPECT_EQ(0, load_observer.session_index_); + EXPECT_EQ(&shell()->web_contents()->GetController(), + load_observer.controller_); +} // Test that a renderer-initiated navigation to an invalid URL does not leave // around a pending entry that could be used in a URL spoof. We test this in // a browser test because our unit test framework incorrectly calls diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc index 32004d1..3d0316f 100644 --- a/content/public/renderer/content_renderer_client.cc +++ b/content/public/renderer/content_renderer_client.cc @@ -98,6 +98,20 @@ bool ContentRendererClient::AllowPopup() { return false; } +#ifdef OS_ANDROID +bool ContentRendererClient::HandleNavigation( + RenderFrame* render_frame, + bool is_content_initiated, + int opener_id, + blink::WebFrame* frame, + const blink::WebURLRequest& request, + blink::WebNavigationType type, + blink::WebNavigationPolicy default_policy, + bool is_redirect) { + return false; +} +#endif + bool ContentRendererClient::ShouldFork(blink::WebLocalFrame* frame, const GURL& url, const std::string& http_method, diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h index fd2f3af..45686b5 100644 --- a/content/public/renderer/content_renderer_client.h +++ b/content/public/renderer/content_renderer_client.h @@ -18,6 +18,8 @@ #include "base/strings/string16.h" #include "content/public/common/content_client.h" #include "third_party/WebKit/public/platform/WebPageVisibilityState.h" +#include "third_party/WebKit/public/web/WebNavigationPolicy.h" +#include "third_party/WebKit/public/web/WebNavigationType.h" #include "ui/base/page_transition_types.h" #include "v8/include/v8.h" @@ -192,6 +194,23 @@ class CONTENT_EXPORT ContentRendererClient { // Returns true if a popup window should be allowed. virtual bool AllowPopup(); +#ifdef OS_ANDROID + // TODO(sgurun) This callback is deprecated and will be removed as soon + // as android webview completes implementation of a resource throttle based + // shouldoverrideurl implementation. See crbug.com/325351 + // + // Returns true if the navigation was handled by the embedder and should be + // ignored by WebKit. This method is used by CEF and android_webview. + virtual bool HandleNavigation(RenderFrame* render_frame, + bool is_content_initiated, + int opener_id, + blink::WebFrame* frame, + const blink::WebURLRequest& request, + blink::WebNavigationType type, + blink::WebNavigationPolicy default_policy, + bool is_redirect); +#endif + // Returns true if we should fork a new process for the given navigation. // If |send_referrer| is set to false (which is the default), no referrer // header will be send for the navigation. Otherwise, the referrer header is diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 88306cc..76ffb69 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -4614,6 +4614,18 @@ WebNavigationPolicy RenderFrameImpl::decidePolicyForNavigation( (pending_navigation_params_ && !pending_navigation_params_->request_params.redirects.empty()); +#ifdef OS_ANDROID + // The handlenavigation API is deprecated and will be removed once + // crbug.com/325351 is resolved. + if (info.urlRequest.url() != GURL(kSwappedOutURL) && + GetContentClient()->renderer()->HandleNavigation( + this, is_content_initiated, render_view_->opener_id_, frame_, + info.urlRequest, info.navigationType, info.defaultPolicy, + is_redirect)) { + return blink::WebNavigationPolicyIgnore; + } +#endif + Referrer referrer( RenderViewImpl::GetReferrerFromRequest(frame_, info.urlRequest)); diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp index 87826d6..bdbd77a 100644 --- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp +++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp @@ -231,8 +231,7 @@ void DocumentLoader::mainReceivedError(const ResourceError& error) m_applicationCacheHost->failedLoadingMainResource(); if (!frameLoader()) return; - if (m_state < MainResourceDone) - m_state = MainResourceDone; + m_state = MainResourceDone; frameLoader()->receivedMainResourceError(this, error); clearMainResourceHandle(); } diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp index fb74068..a578f07 100644 --- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp +++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp @@ -1220,18 +1220,15 @@ void FrameLoader::receivedMainResourceError(DocumentLoader* loader, const Resour if (loader != m_provisionalDocumentLoader) return; detachDocumentLoader(m_provisionalDocumentLoader); - // If the provisional load failed, and we haven't yet rendered anything - // into the frame, then act as though the non-provisional loader failed - // as well. If we don't do this, the main load will never finish. - if (!stateMachine()->committedFirstRealDocumentLoad()) - m_documentLoader->setSentDidFinishLoad(); + m_progressTracker->progressCompleted(); } else { ASSERT(loader == m_documentLoader); if (m_frame->document()->parser()) m_frame->document()->parser()->stopParsing(); - if (!m_documentLoader->sentDidFinishLoad()) { + m_documentLoader->setSentDidFinishLoad(); + if (!m_provisionalDocumentLoader && m_frame->isLoading()) { client()->dispatchDidFailLoad(error, historyCommitType); - m_documentLoader->setSentDidFinishLoad(); + m_progressTracker->progressCompleted(); } } checkCompleted(); |