blob: aeaa73ea221e527d1bebbe289d74d79c8cf10d21 [file] [log] [blame]
// Copyright 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.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.DisabledTest;
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_public.browser.LoadUrlParams;
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;
/**
* Tests for the WebViewClient.shouldOverrideUrlLoading() method.
*/
public class AwContentsClientShouldOverrideUrlLoadingTest extends AwTestBase {
private static final String ABOUT_BLANK_URL = "about:blank";
private static final String DATA_URL = "data:text/html,<div/>";
private static final String REDIRECT_TARGET_PATH = "/redirect_target.html";
private static final String TITLE = "TITLE";
private static final String TAG = "AwContentsClientShouldOverrideUrlLoadingTest";
private TestWebServer mWebServer;
private TestAwContentsClient mContentsClient;
private AwTestContainerView mTestContainerView;
private AwContents mAwContents;
private TestAwContentsClient.ShouldOverrideUrlLoadingHelper mShouldOverrideUrlLoadingHelper;
@Override
protected void setUp() throws Exception {
super.setUp();
mWebServer = TestWebServer.start();
}
@Override
protected void tearDown() throws Exception {
mWebServer.shutdown();
super.tearDown();
}
private void standardSetup() throws Throwable {
mContentsClient = new TestAwContentsClient();
setupWithProvidedContentsClient(mContentsClient);
mShouldOverrideUrlLoadingHelper = mContentsClient.getShouldOverrideUrlLoadingHelper();
}
private void setupWithProvidedContentsClient(AwContentsClient contentsClient) throws Throwable {
mTestContainerView = createAwTestContainerViewOnMainSync(contentsClient);
mAwContents = mTestContainerView.getAwContents();
}
private void clickOnLinkUsingJs() throws Throwable {
enableJavaScriptOnUiThread(mAwContents);
JSUtils.clickOnLinkUsingJs(this, mAwContents,
mContentsClient.getOnEvaluateJavaScriptResultHelper(), "link");
}
// Since this value is read on the UI thread, it's simpler to set it there too.
void setShouldOverrideUrlLoadingReturnValueOnUiThread(final boolean value) throws Throwable {
runTestOnUiThread(new Runnable() {
@Override
public void run() {
mShouldOverrideUrlLoadingHelper.setShouldOverrideUrlLoadingReturnValue(value);
}
});
}
private String getTestPageCommonHeaders() {
return "<title>" + TITLE + "</title> ";
}
private String makeHtmlPageFrom(String headers, String body) {
return CommonResources.makeHtmlPageFrom(getTestPageCommonHeaders() + headers, body);
}
private String getHtmlForPageWithJsAssignLinkTo(String url) {
return makeHtmlPageFrom("",
"<img onclick=\"location.href='" + url + "'\" class=\"big\" id=\"link\" />");
}
private String getHtmlForPageWithJsReplaceLinkTo(String url) {
return makeHtmlPageFrom("",
"<img onclick=\"location.replace('" + url + "');\" class=\"big\" id=\"link\" />");
}
private String getHtmlForPageWithMetaRefreshRedirectTo(String url) {
return makeHtmlPageFrom("<meta http-equiv=\"refresh\" content=\"0;url=" + url + "\" />",
"<div>Meta refresh redirect</div>");
}
private String getHtmlForPageWithJsRedirectTo(String url, String method, int timeout) {
return makeHtmlPageFrom(""
+ "<script>"
+ "function doRedirectAssign() {"
+ "location.href = '" + url + "';"
+ "} "
+ "function doRedirectReplace() {"
+ "location.replace('" + url + "');"
+ "} "
+ "</script>",
String.format("<iframe onLoad=\"setTimeout('doRedirect%s()', %d);\" />",
method, timeout));
}
private String addPageToTestServer(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 mWebServer.setResponse(httpPath, html, headers);
}
private String createRedirectTargetPage() {
return addPageToTestServer(REDIRECT_TARGET_PATH,
makeHtmlPageFrom("", "<div>This is the end of the redirect chain</div>"));
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testNotCalledOnLoadUrl() throws Throwable {
standardSetup();
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testNotCalledOnReload() throws Throwable {
standardSetup();
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
int callCountBeforeReload = mShouldOverrideUrlLoadingHelper.getCallCount();
reloadSync(mAwContents, mContentsClient.getOnPageFinishedHelper());
assertEquals(callCountBeforeReload, mShouldOverrideUrlLoadingHelper.getCallCount());
}
private void waitForNavigationRunnableAndAssertTitleChanged(
Runnable navigationRunnable) throws Exception {
CallbackHelper onPageFinishedHelper = mContentsClient.getOnPageFinishedHelper();
final int callCount = onPageFinishedHelper.getCallCount();
final String oldTitle = getTitleOnUiThread(mAwContents);
getInstrumentation().runOnMainSync(navigationRunnable);
onPageFinishedHelper.waitForCallback(callCount);
assertFalse(oldTitle.equals(getTitleOnUiThread(mAwContents)));
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testNotCalledOnBackForwardNavigation() throws Throwable {
standardSetup();
final String[] pageTitles = new String[] { "page1", "page2", "page3" };
for (String title : pageTitles) {
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageFrom("<title>" + title + "</title>", ""),
"text/html", false);
}
assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());
waitForNavigationRunnableAndAssertTitleChanged(new Runnable() {
@Override
public void run() {
mAwContents.goBack();
}
});
assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());
waitForNavigationRunnableAndAssertTitleChanged(new Runnable() {
@Override
public void run() {
mAwContents.goForward();
}
});
assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());
waitForNavigationRunnableAndAssertTitleChanged(new Runnable() {
@Override
public void run() {
mAwContents.goBackOrForward(-2);
}
});
assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());
waitForNavigationRunnableAndAssertTitleChanged(new Runnable() {
@Override
public void run() {
mAwContents.goBackOrForward(1);
}
});
assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCantBlockLoads() throws Throwable {
standardSetup();
setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(getTestPageCommonHeaders(),
DATA_URL), "text/html", false);
assertEquals(TITLE, getTitleOnUiThread(mAwContents));
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledBeforeOnPageStarted() throws Throwable {
standardSetup();
OnPageStartedHelper onPageStartedHelper = mContentsClient.getOnPageStartedHelper();
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
final int shouldOverrideUrlLoadingCallCount =
mShouldOverrideUrlLoadingHelper.getCallCount();
final int onPageStartedCallCount = onPageStartedHelper.getCallCount();
setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
assertEquals(onPageStartedCallCount, onPageStartedHelper.getCallCount());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testDoesNotCauseOnReceivedError() throws Throwable {
standardSetup();
OnReceivedErrorHelper onReceivedErrorHelper = mContentsClient.getOnReceivedErrorHelper();
final int onReceivedErrorCallCount = onReceivedErrorHelper.getCallCount();
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
final int shouldOverrideUrlLoadingCallCount =
mShouldOverrideUrlLoadingHelper.getCallCount();
setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
setShouldOverrideUrlLoadingReturnValueOnUiThread(false);
// After we load this URL we're certain that any in-flight callbacks for the previous
// navigation have been delivered.
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), ABOUT_BLANK_URL);
assertEquals(onReceivedErrorCallCount, onReceivedErrorHelper.getCallCount());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testNotCalledForAnchorNavigations() throws Throwable {
doTestNotCalledForAnchorNavigations(false);
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testNotCalledForAnchorNavigationsWithNonHierarchicalScheme() throws Throwable {
doTestNotCalledForAnchorNavigations(true);
}
private void doTestNotCalledForAnchorNavigations(boolean useLoadData) throws Throwable {
standardSetup();
final String anchorLinkPath = "/anchor_link.html";
final String anchorLinkUrl = mWebServer.getResponseUrl(anchorLinkPath);
addPageToTestServer(anchorLinkPath,
CommonResources.makeHtmlPageWithSimpleLinkTo(anchorLinkUrl + "#anchor"));
if (useLoadData) {
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo("#anchor"), "text/html", false);
} else {
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), anchorLinkUrl);
}
final int shouldOverrideUrlLoadingCallCount =
mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
// After we load this URL we're certain that any in-flight callbacks for the previous
// navigation have been delivered.
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), ABOUT_BLANK_URL);
assertEquals(shouldOverrideUrlLoadingCallCount,
mShouldOverrideUrlLoadingHelper.getCallCount());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledWhenLinkClicked() throws Throwable {
standardSetup();
// We can't go to about:blank from here because we'd get a cross-origin error.
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
assertEquals(DATA_URL, mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
/*
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
crbug.com/462306
*/
@DisabledTest
public void testCalledWhenTopLevelAboutBlankNavigation() throws Throwable {
standardSetup();
final String httpPath = "/page_with_about_blank_navigation";
final String httpPathOnServer = mWebServer.getResponseUrl(httpPath);
addPageToTestServer(httpPath,
CommonResources.makeHtmlPageWithSimpleLinkTo(ABOUT_BLANK_URL));
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
httpPathOnServer);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
assertEquals(ABOUT_BLANK_URL,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledWhenSelfLinkClicked() throws Throwable {
standardSetup();
final String httpPath = "/page_with_link_to_self.html";
final String httpPathOnServer = mWebServer.getResponseUrl(httpPath);
addPageToTestServer(httpPath,
CommonResources.makeHtmlPageWithSimpleLinkTo(httpPathOnServer));
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
httpPathOnServer);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
assertEquals(httpPathOnServer,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledWhenNavigatingFromJavaScriptUsingAssign()
throws Throwable {
standardSetup();
enableJavaScriptOnUiThread(mAwContents);
final String redirectTargetUrl = createRedirectTargetPage();
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
getHtmlForPageWithJsAssignLinkTo(redirectTargetUrl), "text/html", false);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledWhenNavigatingFromJavaScriptUsingReplace()
throws Throwable {
standardSetup();
enableJavaScriptOnUiThread(mAwContents);
final String redirectTargetUrl = createRedirectTargetPage();
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
getHtmlForPageWithJsReplaceLinkTo(redirectTargetUrl), "text/html", false);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
// It's not a server-side redirect.
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testPassesCorrectUrl() throws Throwable {
standardSetup();
final String redirectTargetUrl = createRedirectTargetPage();
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(redirectTargetUrl), "text/html",
false);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
assertEquals(redirectTargetUrl,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
// It's not a server-side redirect.
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCanIgnoreLoading() throws Throwable {
standardSetup();
final String redirectTargetUrl = createRedirectTargetPage();
final String pageWithLinkToIgnorePath = "/page_with_link_to_ignore.html";
final String pageWithLinkToIgnoreUrl = addPageToTestServer(pageWithLinkToIgnorePath,
CommonResources.makeHtmlPageWithSimpleLinkTo(redirectTargetUrl));
final String synchronizationPath = "/sync.html";
final String synchronizationUrl = addPageToTestServer(synchronizationPath,
CommonResources.makeHtmlPageWithSimpleLinkTo(redirectTargetUrl));
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
pageWithLinkToIgnoreUrl);
setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
// Some time around here true should be returned from the shouldOverrideUrlLoading
// callback causing the navigation caused by calling clickOnLinkUsingJs to be ignored.
// We validate this by checking which pages were loaded on the server.
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
setShouldOverrideUrlLoadingReturnValueOnUiThread(false);
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), synchronizationUrl);
assertEquals(1, mWebServer.getRequestCount(pageWithLinkToIgnorePath));
assertEquals(1, mWebServer.getRequestCount(synchronizationPath));
assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledForDataUrl() throws Throwable {
standardSetup();
final String dataUrl =
"data:text/html;base64,"
+ "PGh0bWw+PGhlYWQ+PHRpdGxlPmRhdGFVcmxUZXN0QmFzZTY0PC90aXRsZT48"
+ "L2hlYWQ+PC9odG1sPg==";
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(dataUrl), "text/html", false);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
assertTrue("Expected URL that starts with 'data:' but got: <"
+ mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl() + "> instead.",
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl().startsWith(
"data:"));
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledForUnsupportedSchemes() throws Throwable {
standardSetup();
final String unsupportedSchemeUrl = "foobar://resource/1";
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(unsupportedSchemeUrl), "text/html",
false);
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
assertEquals(unsupportedSchemeUrl,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testNotCalledForPostNavigations() throws Throwable {
// The reason POST requests are excluded is BUG 155250.
standardSetup();
final String redirectTargetUrl = createRedirectTargetPage();
final String postLinkUrl = addPageToTestServer("/page_with_post_link.html",
CommonResources.makeHtmlPageWithSimplePostFormTo(redirectTargetUrl));
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), postLinkUrl);
final int shouldOverrideUrlLoadingCallCount =
mShouldOverrideUrlLoadingHelper.getCallCount();
assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
clickOnLinkUsingJs();
// Wait for the target URL to be fetched from the server.
poll(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
}
});
// Since the targetURL was loaded from the test server it means all processing related
// to dispatching a shouldOverrideUrlLoading callback had finished and checking the call
// is stable.
assertEquals(shouldOverrideUrlLoadingCallCount,
mShouldOverrideUrlLoadingHelper.getCallCount());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledFor302AfterPostNavigations() throws Throwable {
// The reason POST requests are excluded is BUG 155250.
standardSetup();
final String redirectTargetUrl = createRedirectTargetPage();
final String postToGetRedirectUrl = mWebServer.setRedirect("/302.html", redirectTargetUrl);
final String postLinkUrl = addPageToTestServer("/page_with_post_link.html",
CommonResources.makeHtmlPageWithSimplePostFormTo(postToGetRedirectUrl));
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), postLinkUrl);
final int shouldOverrideUrlLoadingCallCount =
mShouldOverrideUrlLoadingHelper.getCallCount();
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
// Wait for the target URL to be fetched from the server.
poll(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
}
});
assertEquals(redirectTargetUrl,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertTrue(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testNotCalledForIframeHttpNavigations() throws Throwable {
standardSetup();
final String iframeRedirectTargetUrl = createRedirectTargetPage();
final String iframeRedirectUrl =
mWebServer.setRedirect("/302.html", iframeRedirectTargetUrl);
final String pageWithIframeUrl =
addPageToTestServer("/iframe_intercept.html",
makeHtmlPageFrom("", "<iframe src=\"" + iframeRedirectUrl + "\" />"));
final int shouldOverrideUrlLoadingCallCount =
mShouldOverrideUrlLoadingHelper.getCallCount();
assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithIframeUrl);
// Wait for the redirect target URL to be fetched from the server.
poll(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
}
});
assertEquals(shouldOverrideUrlLoadingCallCount,
mShouldOverrideUrlLoadingHelper.getCallCount());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledForIframeUnsupportedSchemeNavigations() throws Throwable {
standardSetup();
final String unsupportedSchemeUrl = "foobar://resource/1";
final String pageWithIframeUrl =
addPageToTestServer("/iframe_intercept.html",
makeHtmlPageFrom("", "<iframe src=\"" + unsupportedSchemeUrl + "\" />"));
final int shouldOverrideUrlLoadingCallCount =
mShouldOverrideUrlLoadingHelper.getCallCount();
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithIframeUrl);
mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
assertEquals(unsupportedSchemeUrl,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertFalse(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
/**
* Worker method for the various redirect tests.
*
* Calling this will first load the redirect URL built from redirectFilePath, query and
* locationFilePath and assert that we get a override callback for the destination.
* The second part of the test loads a page that contains a link which points at the redirect
* URL. We expect two callbacks - one for the redirect link and another for the destination.
*/
private void doTestCalledOnRedirect(String redirectUrl, String redirectTarget,
boolean serverSideRedirect) throws Throwable {
standardSetup();
final String pageTitle = "doTestCalledOnRedirect page";
final String pageWithLinkToRedirectUrl = addPageToTestServer(
"/page_with_link_to_redirect.html", CommonResources.makeHtmlPageWithSimpleLinkTo(
"<title>" + pageTitle + "</title>", redirectUrl));
enableJavaScriptOnUiThread(mAwContents);
// There is a slight difference between navigations caused by calling load and navigations
// caused by clicking on a link:
//
// * when using load the navigation is treated as if it came from the URL bar (has the
// navigation type TYPED, doesn't have the has_user_gesture flag); thus the navigation
// itself is not reported via shouldOverrideUrlLoading, but then if it has caused a
// redirect, the redirect itself is reported;
//
// * when clicking on a link the navigation has the LINK type and has_user_gesture depends
// on whether it was a real click done by the user, or has it been done by JS; on click,
// both the initial navigation and the redirect are reported via
// shouldOverrideUrlLoading.
int directLoadCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), redirectUrl);
mShouldOverrideUrlLoadingHelper.waitForCallback(directLoadCallCount, 1);
assertEquals(redirectTarget,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertEquals(serverSideRedirect, mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
// Test clicking with JS, hasUserGesture must be false.
int indirectLoadCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
pageWithLinkToRedirectUrl);
assertEquals(indirectLoadCallCount, mShouldOverrideUrlLoadingHelper.getCallCount());
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(indirectLoadCallCount, 1);
assertEquals(redirectUrl,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
mShouldOverrideUrlLoadingHelper.waitForCallback(indirectLoadCallCount + 1, 1);
assertEquals(redirectTarget,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertEquals(serverSideRedirect, mShouldOverrideUrlLoadingHelper.isRedirect());
assertFalse(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
// Make sure the redirect target page has finished loading.
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return !mAwContents.getTitle().equals(pageTitle);
}
});
indirectLoadCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
loadUrlAsync(mAwContents, pageWithLinkToRedirectUrl);
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return mAwContents.getTitle().equals(pageTitle);
}
});
assertEquals(indirectLoadCallCount, mShouldOverrideUrlLoadingHelper.getCallCount());
// Simulate touch, hasUserGesture must be true only on the first call.
DOMUtils.clickNode(this, mAwContents.getContentViewCore(), "link");
mShouldOverrideUrlLoadingHelper.waitForCallback(indirectLoadCallCount, 1);
assertEquals(redirectUrl,
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertTrue(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
mShouldOverrideUrlLoadingHelper.waitForCallback(indirectLoadCallCount + 1, 1);
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());
assertTrue(mShouldOverrideUrlLoadingHelper.isMainFrame());
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledOn302Redirect() throws Throwable {
final String redirectTargetUrl = createRedirectTargetPage();
final String redirectUrl = mWebServer.setRedirect("/302.html", redirectTargetUrl);
doTestCalledOnRedirect(redirectUrl, redirectTargetUrl, true);
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledOnMetaRefreshRedirect() throws Throwable {
final String redirectTargetUrl = createRedirectTargetPage();
final String redirectUrl = addPageToTestServer("/meta_refresh.html",
getHtmlForPageWithMetaRefreshRedirectTo(redirectTargetUrl));
doTestCalledOnRedirect(redirectUrl, redirectTargetUrl, false);
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledOnJavaScriptLocationImmediateAssignRedirect()
throws Throwable {
final String redirectTargetUrl = createRedirectTargetPage();
final String redirectUrl = addPageToTestServer("/js_immediate_assign.html",
getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Assign", 0));
doTestCalledOnRedirect(redirectUrl, redirectTargetUrl, false);
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledOnJavaScriptLocationImmediateReplaceRedirect()
throws Throwable {
final String redirectTargetUrl = createRedirectTargetPage();
final String redirectUrl = addPageToTestServer("/js_immediate_replace.html",
getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Replace", 0));
doTestCalledOnRedirect(redirectUrl, redirectTargetUrl, false);
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledOnJavaScriptLocationDelayedAssignRedirect()
throws Throwable {
final String redirectTargetUrl = createRedirectTargetPage();
final String redirectUrl = addPageToTestServer("/js_delayed_assign.html",
getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Assign", 100));
doTestCalledOnRedirect(redirectUrl, redirectTargetUrl, false);
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testCalledOnJavaScriptLocationDelayedReplaceRedirect()
throws Throwable {
final String redirectTargetUrl = createRedirectTargetPage();
final String redirectUrl = addPageToTestServer("/js_delayed_replace.html",
getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Replace", 100));
doTestCalledOnRedirect(redirectUrl, redirectTargetUrl, false);
}
@SmallTest
@Feature({"AndroidWebView", "Navigation"})
public void testDoubleNavigateDoesNotSuppressInitialNavigate() throws Throwable {
final String jsUrl = "javascript:try{console.log('processed js loadUrl');}catch(e){};";
standardSetup();
// Do a double navigagtion, the second being an effective no-op, in quick succession (i.e.
// without yielding the main thread inbetween).
int currentCallCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mAwContents.loadUrl(LoadUrlParams.createLoadDataParams(
CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html",
false));
mAwContents.loadUrl(new LoadUrlParams(jsUrl));
}
});
mContentsClient.getOnPageFinishedHelper().waitForCallback(currentCallCount, 1,
WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());
}
@SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
@SmallTest
@Feature({"AndroidWebView"})
public void testCallDestroyInCallback() throws Throwable {
class DestroyInCallbackClient extends TestAwContentsClient {
@Override
public boolean shouldOverrideUrlLoading(AwContentsClient.AwWebResourceRequest request) {
mAwContents.destroy();
return super.shouldOverrideUrlLoading(request);
}
}
mContentsClient = new DestroyInCallbackClient();
setupWithProvidedContentsClient(mContentsClient);
mShouldOverrideUrlLoadingHelper = mContentsClient.getShouldOverrideUrlLoadingHelper();
OnReceivedErrorHelper onReceivedErrorHelper = mContentsClient.getOnReceivedErrorHelper();
int onReceivedErrorCallCount = onReceivedErrorHelper.getCallCount();
loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
int shouldOverrideUrlLoadingCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
clickOnLinkUsingJs();
mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return AwContents.getNativeInstanceCount() == 0;
}
});
}
@SmallTest
@Feature({"AndroidWebView"})
public void testNullContentsClientWithServerRedirect() throws Throwable {
try {
// The test will fire real intents through the test activity.
// Need to temporarily suppress startActivity otherwise there will be a
// handler selection window and the test can't dismiss that.
getActivity().setIgnoreStartActivity(true);
final String testUrl = mWebServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
setupWithProvidedContentsClient(new NullContentsClient() {
@Override
public boolean hasWebViewClient() {
return false;
}
});
loadUrlAsync(mAwContents, testUrl);
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return mAwContents.getTitle().equals(CommonResources.ABOUT_TITLE);
}
});
assertNull(getActivity().getLastSentIntent());
// Now the server will redirect path1 to path2. Path2 will load ABOUT_HTML.
// AwContents should create an intent for the server initiated redirection.
final String path1 = "/from.html";
final String path2 = "/to.html";
final String fromUrl = mWebServer.setRedirect(path1, path2);
final String toUrl = mWebServer.setResponse(
path2, CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
loadUrlAsync(mAwContents, fromUrl);
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return getActivity().getLastSentIntent() != null;
}
});
assertEquals(toUrl, getActivity().getLastSentIntent().getData().toString());
} finally {
getActivity().setIgnoreStartActivity(false);
}
}
@SmallTest
@Feature({"AndroidWebView"})
public void testNullContentsClientOpenLink() throws Throwable {
try {
// The test will fire real intents through the test activity.
// Need to temporarily suppress startActivity otherwise there will be a
// handler selection window and the test can't dismiss that.
getActivity().setIgnoreStartActivity(true);
final String testUrl = mWebServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
CommonResources.ABOUT_HTML, CommonResources.getTextHtmlHeaders(true));
setupWithProvidedContentsClient(new NullContentsClient() {
@Override
public boolean hasWebViewClient() {
return false;
}
});
mAwContents.getSettings().setJavaScriptEnabled(true);
final String pageTitle = "Click Title";
final String htmlWithLink = "<html><title>" + pageTitle + "</title>"
+ "<body><a id='link' href='" + testUrl + "'>Click this!</a></body></html>";
final String urlWithLink = mWebServer.setResponse(
"/html_with_link.html", htmlWithLink, CommonResources.getTextHtmlHeaders(true));
loadUrlAsync(mAwContents, urlWithLink);
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return mAwContents.getTitle().equals(pageTitle);
}
});
// Executing JS code that tries to navigate somewhere should not create an intent.
assertEquals("\"" + testUrl + "\"", JSUtils.executeJavaScriptAndWaitForResult(
this, mAwContents, new OnEvaluateJavaScriptResultHelper(),
"document.location.href='" + testUrl + "'"));
assertNull(getActivity().getLastSentIntent());
// Clicking on a link should create an intent.
DOMUtils.clickNode(this, mAwContents.getContentViewCore(), "link");
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return getActivity().getLastSentIntent() != null;
}
});
assertEquals(testUrl, getActivity().getLastSentIntent().getData().toString());
} finally {
getActivity().setIgnoreStartActivity(false);
}
}
@SmallTest
@Feature({"AndroidWebView"})
public void testNullContentsClientClickableContent() throws Throwable {
try {
// The test will fire real intents through the test activity.
// Need to temporarily suppress startActivity otherwise there will be a
// handler selection window and the test can't dismiss that.
getActivity().setIgnoreStartActivity(true);
setupWithProvidedContentsClient(new NullContentsClient() {
@Override
public boolean hasWebViewClient() {
return false;
}
});
final String pageTitle = "Click Title";
final String testEmail = "nobody@example.org";
final String testUrl = mWebServer.setResponse("/email_test.html",
"<html><head><title>" + pageTitle + "</title></head>"
+ "<body><span id='email'>" + testEmail + "</span></body>", null);
// JS is required for the click simulator.
mAwContents.getSettings().setJavaScriptEnabled(true);
loadUrlAsync(mAwContents, testUrl);
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return mAwContents.getTitle().equals(pageTitle);
}
});
// Clicking on an email should create an intent.
DOMUtils.clickNode(this, mAwContents.getContentViewCore(), "email");
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return getActivity().getLastSentIntent() != null;
}
});
assertEquals("mailto:" + testEmail.replace("@", "%40"),
getActivity().getLastSentIntent().getData().toString());
} finally {
getActivity().setIgnoreStartActivity(false);
}
}
private void doTestClickableContent(boolean inMainFrame) throws Throwable {
standardSetup();
final String testEmail = "nobody@example.org";
final String findEmailJs = inMainFrame
? "document.getElementById(\"email\")"
: "window.frames[0].document.getElementById(\"email\")";
final String pageHtml = inMainFrame
? "<html><body onload='document.title=" + findEmailJs + ".innerText'>"
+ "<span id='email'>" + testEmail + "</span></body></html>"
: "<html>"
+ "<body style='margin:0;' onload='document.title=" + findEmailJs + ".innerText'>"
+ " <iframe style='border:none;' srcdoc=\""
+ " <body style='margin:0;'><span id='email'>" + testEmail + "</span></body>"
+ "\" src='iframe.html'></iframe>"
+ "</body></html>";
final String testUrl = mWebServer.setResponse("/email_test.html", pageHtml, null);
// JS is required for the click simulator.
mAwContents.getSettings().setJavaScriptEnabled(true);
loadUrlAsync(mAwContents, testUrl);
pollOnUiThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return mAwContents.getTitle().equals(testEmail);
}
});
int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
DOMUtils.clickNodeByJs(this, mAwContents.getContentViewCore(), findEmailJs);
mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
assertEquals("mailto:" + testEmail.replace("@", "%40"),
mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
assertFalse(mShouldOverrideUrlLoadingHelper.isRedirect());
assertTrue(mShouldOverrideUrlLoadingHelper.hasUserGesture());
assertEquals(inMainFrame, mShouldOverrideUrlLoadingHelper.isMainFrame());
}
@SmallTest
@Feature({"AndroidWebView"})
public void testClickableContent() throws Throwable {
doTestClickableContent(true);
}
@SmallTest
@Feature({"AndroidWebView"})
public void testClickableContentInIframe() throws Throwable {
doTestClickableContent(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());
}
}