AwContentsClientShouldOverrideUrlLoadingTest.java revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.android_webview.test;
6
7import android.os.Bundle;
8import android.os.SystemClock;
9import android.test.suitebuilder.annotation.SmallTest;
10import android.util.Pair;
11import android.view.MotionEvent;
12import android.util.Log;
13
14import org.chromium.android_webview.AwContents;
15import org.chromium.android_webview.test.util.CommonResources;
16import org.chromium.android_webview.test.util.JSUtils;
17import org.chromium.base.test.util.Feature;
18import org.chromium.content.browser.NavigationHistory;
19import org.chromium.content.browser.LoadUrlParams;
20import org.chromium.content.browser.test.util.CallbackHelper;
21import org.chromium.content.browser.test.util.Criteria;
22import org.chromium.content.browser.test.util.CriteriaHelper;
23import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper;
24import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageStartedHelper;
25import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnReceivedErrorHelper;
26import org.chromium.net.test.util.TestWebServer;
27
28import java.net.URLEncoder;
29import java.util.ArrayList;
30import java.util.List;
31import java.util.concurrent.Callable;
32import java.util.concurrent.TimeUnit;
33
34/**
35 * Tests for the WebViewClient.shouldOverrideUrlLoading() method.
36 */
37public class AwContentsClientShouldOverrideUrlLoadingTest extends AwTestBase {
38    private final static String ABOUT_BLANK_URL = "about:blank";
39    private final static String DATA_URL = "data:text/html,<div/>";
40    private final static String REDIRECT_TARGET_PATH = "/redirect_target.html";
41    private final static String TITLE = "TITLE";
42
43    private static final long TEST_TIMEOUT = 20000L;
44    private static final long CHECK_INTERVAL = 100;
45
46    private static class TestAwContentsClient
47            extends org.chromium.android_webview.test.TestAwContentsClient {
48
49        public static class ShouldOverrideUrlLoadingHelper extends CallbackHelper {
50            private String mShouldOverrideUrlLoadingUrl;
51            private String mPreviousShouldOverrideUrlLoadingUrl;
52            private boolean mShouldOverrideUrlLoadingReturnValue = false;
53            void setShouldOverrideUrlLoadingUrl(String url) {
54                mShouldOverrideUrlLoadingUrl = url;
55            }
56            void setPreviousShouldOverrideUrlLoadingUrl(String url) {
57                mPreviousShouldOverrideUrlLoadingUrl = url;
58            }
59            void setShouldOverrideUrlLoadingReturnValue(boolean value) {
60                mShouldOverrideUrlLoadingReturnValue = value;
61            }
62            public String getShouldOverrideUrlLoadingUrl() {
63                assert getCallCount() > 0;
64                return mShouldOverrideUrlLoadingUrl;
65            }
66            public String getPreviousShouldOverrideUrlLoadingUrl() {
67                assert getCallCount() > 1;
68                return mPreviousShouldOverrideUrlLoadingUrl;
69            }
70            public boolean getShouldOverrideUrlLoadingReturnValue() {
71                return mShouldOverrideUrlLoadingReturnValue;
72            }
73            public void notifyCalled(String url) {
74                mPreviousShouldOverrideUrlLoadingUrl = mShouldOverrideUrlLoadingUrl;
75                mShouldOverrideUrlLoadingUrl = url;
76                notifyCalled();
77            }
78        }
79
80        @Override
81        public boolean shouldOverrideUrlLoading(String url) {
82            super.shouldOverrideUrlLoading(url);
83            boolean returnValue =
84                mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingReturnValue();
85            mShouldOverrideUrlLoadingHelper.notifyCalled(url);
86            return returnValue;
87        }
88
89        private ShouldOverrideUrlLoadingHelper mShouldOverrideUrlLoadingHelper;
90
91        public TestAwContentsClient() {
92            mShouldOverrideUrlLoadingHelper = new ShouldOverrideUrlLoadingHelper();
93        }
94
95        public ShouldOverrideUrlLoadingHelper getShouldOverrideUrlLoadingHelper() {
96            return mShouldOverrideUrlLoadingHelper;
97        }
98    }
99
100    private TestWebServer mWebServer;
101
102    @Override
103    protected void setUp() throws Exception {
104        super.setUp();
105        mWebServer = new TestWebServer(false);
106    }
107
108    @Override
109    protected void tearDown() throws Exception {
110        mWebServer.shutdown();
111        super.tearDown();
112    }
113
114    private void clickOnLinkUsingJs(final AwContents awContents,
115            final TestAwContentsClient contentsClient) throws Throwable {
116        enableJavaScriptOnUiThread(awContents);
117        JSUtils.clickOnLinkUsingJs(this, awContents,
118                contentsClient.getOnEvaluateJavaScriptResultHelper(), "link");
119    }
120
121    // Since this value is read on the UI thread, it's simpler to set it there too.
122    void setShouldOverrideUrlLoadingReturnValueOnUiThread(
123            final TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideHelper,
124            final boolean value) throws Throwable {
125        runTestOnUiThread(new Runnable() {
126            @Override
127            public void run() {
128                shouldOverrideHelper.setShouldOverrideUrlLoadingReturnValue(value);
129            }
130        });
131    }
132
133    private String makeHtmlPageFrom(String headers, String body) {
134        return CommonResources.makeHtmlPageFrom("<title>" + TITLE + "</title> " + headers, body);
135    }
136
137    private String getHtmlForPageWithSimpleLinkTo(String destination) {
138        return makeHtmlPageFrom("",
139                        "<a href=\"" + destination + "\" id=\"link\">" +
140                           "<img class=\"big\" />" +
141                        "</a>");
142    }
143
144    private String getHtmlForPageWithJsAssignLinkTo(String url) {
145        return makeHtmlPageFrom("",
146                "<img onclick=\"location.href='" + url + "'\" class=\"big\" id=\"link\" />");
147    }
148
149    private String getHtmlForPageWithJsReplaceLinkTo(String url) {
150        return makeHtmlPageFrom("",
151                "<img onclick=\"location.replace('" + url + "');\" class=\"big\" id=\"link\" />");
152    }
153
154    private String getHtmlForPageWithMetaRefreshRedirectTo(String url) {
155        return makeHtmlPageFrom("<meta http-equiv=\"refresh\" content=\"0;url=" + url + "\" />",
156                "<div>Meta refresh redirect</div>");
157    }
158
159    private String getHtmlForPageWithJsRedirectTo(String url, String method, int timeout) {
160        return makeHtmlPageFrom(
161                "<script>" +
162                  "function doRedirectAssign() {" +
163                    "location.href = '" + url + "';" +
164                  "} " +
165                  "function doRedirectReplace() {" +
166                    "location.replace('" + url + "');" +
167                  "} "+
168                "</script>",
169                String.format("<iframe onLoad=\"setTimeout('doRedirect%s()', %d);\" />",
170                    method, timeout));
171    }
172
173    private String getHtmlForPageWithSimplePostFormTo(String destination) {
174        return makeHtmlPageFrom("",
175                "<form action=\"" + destination + "\" method=\"post\">" +
176                  "<input type=\"submit\" value=\"post\" id=\"link\">"+
177                "</form>");
178    }
179
180    private String addPageToTestServer(TestWebServer webServer, String httpPath, String html) {
181        List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
182        headers.add(Pair.create("Content-Type", "text/html"));
183        headers.add(Pair.create("Cache-Control", "no-store"));
184        return webServer.setResponse(httpPath, html, headers);
185    }
186
187    private String createRedirectTargetPage(TestWebServer webServer) {
188        return addPageToTestServer(webServer, REDIRECT_TARGET_PATH,
189                makeHtmlPageFrom("", "<div>This is the end of the redirect chain</div>"));
190    }
191
192    @SmallTest
193    @Feature({"AndroidWebView", "Navigation"})
194    public void testNotCalledOnLoadUrl() throws Throwable {
195        final TestAwContentsClient contentsClient = new TestAwContentsClient();
196        final AwTestContainerView testContainerView =
197            createAwTestContainerViewOnMainSync(contentsClient);
198        final AwContents awContents = testContainerView.getAwContents();
199        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
200            contentsClient.getShouldOverrideUrlLoadingHelper();
201
202        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
203                getHtmlForPageWithSimpleLinkTo(DATA_URL), "text/html", false);
204
205        assertEquals(0, shouldOverrideUrlLoadingHelper.getCallCount());
206    }
207
208    private void waitForNavigationRunnableAndAssertTitleChanged(AwContents awContents,
209            CallbackHelper onPageFinishedHelper,
210            Runnable navigationRunnable) throws Exception {
211        final int callCount = onPageFinishedHelper.getCallCount();
212        final String oldTitle = getTitleOnUiThread(awContents);
213        getInstrumentation().runOnMainSync(navigationRunnable);
214        onPageFinishedHelper.waitForCallback(callCount);
215        assertFalse(oldTitle.equals(getTitleOnUiThread(awContents)));
216    }
217
218    @SmallTest
219    @Feature({"AndroidWebView", "Navigation"})
220    public void testNotCalledOnBackForwardNavigation() throws Throwable {
221        final TestAwContentsClient contentsClient = new TestAwContentsClient();
222        final AwTestContainerView testContainerView =
223            createAwTestContainerViewOnMainSync(contentsClient);
224        final AwContents awContents = testContainerView.getAwContents();
225        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
226            contentsClient.getShouldOverrideUrlLoadingHelper();
227        final String[] pageTitles = new String[] { "page1", "page2", "page3" };
228
229        for (String title: pageTitles) {
230            loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
231                    CommonResources.makeHtmlPageFrom("<title>" + title + "</title>", ""),
232                    "text/html", false);
233        }
234        assertEquals(0, shouldOverrideUrlLoadingHelper.getCallCount());
235
236        waitForNavigationRunnableAndAssertTitleChanged(awContents,
237                contentsClient.getOnPageFinishedHelper(), new Runnable() {
238            @Override
239            public void run() {
240                awContents.goBack();
241            }
242        });
243        assertEquals(0, shouldOverrideUrlLoadingHelper.getCallCount());
244
245        waitForNavigationRunnableAndAssertTitleChanged(awContents,
246                contentsClient.getOnPageFinishedHelper(), new Runnable() {
247            @Override
248            public void run() {
249                awContents.goForward();
250            }
251        });
252        assertEquals(0, shouldOverrideUrlLoadingHelper.getCallCount());
253
254        waitForNavigationRunnableAndAssertTitleChanged(awContents,
255                contentsClient.getOnPageFinishedHelper(), new Runnable() {
256            @Override
257            public void run() {
258                awContents.goBackOrForward(-2);
259            }
260        });
261        assertEquals(0, shouldOverrideUrlLoadingHelper.getCallCount());
262
263        waitForNavigationRunnableAndAssertTitleChanged(awContents,
264                contentsClient.getOnPageFinishedHelper(), new Runnable() {
265            @Override
266            public void run() {
267                awContents.goBackOrForward(1);
268            }
269        });
270        assertEquals(0, shouldOverrideUrlLoadingHelper.getCallCount());
271    }
272
273    @SmallTest
274    @Feature({"AndroidWebView", "Navigation"})
275    public void testCantBlockLoads() throws Throwable {
276        final TestAwContentsClient contentsClient = new TestAwContentsClient();
277        final AwTestContainerView testContainerView =
278            createAwTestContainerViewOnMainSync(contentsClient);
279        final AwContents awContents = testContainerView.getAwContents();
280        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
281            contentsClient.getShouldOverrideUrlLoadingHelper();
282
283        setShouldOverrideUrlLoadingReturnValueOnUiThread(shouldOverrideUrlLoadingHelper, true);
284
285        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
286                getHtmlForPageWithSimpleLinkTo(DATA_URL), "text/html", false);
287
288        assertEquals(TITLE, getTitleOnUiThread(awContents));
289    }
290
291    @SmallTest
292    @Feature({"AndroidWebView", "Navigation"})
293    public void testCalledBeforeOnPageStarted() throws Throwable {
294        final TestAwContentsClient contentsClient = new TestAwContentsClient();
295        final AwTestContainerView testContainerView =
296            createAwTestContainerViewOnMainSync(contentsClient);
297        final AwContents awContents = testContainerView.getAwContents();
298        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
299            contentsClient.getShouldOverrideUrlLoadingHelper();
300        OnPageStartedHelper onPageStartedHelper = contentsClient.getOnPageStartedHelper();
301
302        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
303                getHtmlForPageWithSimpleLinkTo(DATA_URL), "text/html", false);
304
305        final int shouldOverrideUrlLoadingCallCount = shouldOverrideUrlLoadingHelper.getCallCount();
306        final int onPageStartedCallCount = onPageStartedHelper.getCallCount();
307        setShouldOverrideUrlLoadingReturnValueOnUiThread(shouldOverrideUrlLoadingHelper, true);
308        clickOnLinkUsingJs(awContents, contentsClient);
309
310        shouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
311        assertEquals(onPageStartedCallCount, onPageStartedHelper.getCallCount());
312    }
313
314
315    @SmallTest
316    @Feature({"AndroidWebView", "Navigation"})
317    public void testDoesNotCauseOnReceivedError() throws Throwable {
318        final TestAwContentsClient contentsClient = new TestAwContentsClient();
319        final AwTestContainerView testContainerView =
320            createAwTestContainerViewOnMainSync(contentsClient);
321        final AwContents awContents = testContainerView.getAwContents();
322        final TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
323            contentsClient.getShouldOverrideUrlLoadingHelper();
324        OnReceivedErrorHelper onReceivedErrorHelper = contentsClient.getOnReceivedErrorHelper();
325        final int onReceivedErrorCallCount = onReceivedErrorHelper.getCallCount();
326
327        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
328                getHtmlForPageWithSimpleLinkTo(DATA_URL), "text/html", false);
329
330        final int shouldOverrideUrlLoadingCallCount = shouldOverrideUrlLoadingHelper.getCallCount();
331
332        setShouldOverrideUrlLoadingReturnValueOnUiThread(shouldOverrideUrlLoadingHelper, true);
333
334        clickOnLinkUsingJs(awContents, contentsClient);
335
336        shouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
337
338        setShouldOverrideUrlLoadingReturnValueOnUiThread(shouldOverrideUrlLoadingHelper, false);
339
340        // After we load this URL we're certain that any in-flight callbacks for the previous
341        // navigation have been delivered.
342        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), ABOUT_BLANK_URL);
343
344        assertEquals(onReceivedErrorCallCount, onReceivedErrorHelper.getCallCount());
345    }
346
347    @SmallTest
348    @Feature({"AndroidWebView", "Navigation"})
349    public void testNotCalledForAnchorNavigations() throws Throwable {
350        final TestAwContentsClient contentsClient = new TestAwContentsClient();
351        final AwTestContainerView testContainerView =
352            createAwTestContainerViewOnMainSync(contentsClient);
353        final AwContents awContents = testContainerView.getAwContents();
354        final TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
355            contentsClient.getShouldOverrideUrlLoadingHelper();
356
357        final String anchorLinkPath = "/anchor_link.html";
358        final String anchorLinkUrl = mWebServer.getResponseUrl(anchorLinkPath);
359        addPageToTestServer(mWebServer, anchorLinkPath,
360                getHtmlForPageWithSimpleLinkTo(anchorLinkUrl + "#anchor"));
361
362        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), anchorLinkUrl);
363
364        final int shouldOverrideUrlLoadingCallCount =
365            shouldOverrideUrlLoadingHelper.getCallCount();
366
367        clickOnLinkUsingJs(awContents, contentsClient);
368
369        // After we load this URL we're certain that any in-flight callbacks for the previous
370        // navigation have been delivered.
371        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), ABOUT_BLANK_URL);
372
373        assertEquals(shouldOverrideUrlLoadingCallCount,
374                shouldOverrideUrlLoadingHelper.getCallCount());
375    }
376
377    @SmallTest
378    @Feature({"AndroidWebView", "Navigation"})
379    public void testCalledWhenLinkClicked() throws Throwable {
380        final TestAwContentsClient contentsClient = new TestAwContentsClient();
381        final AwTestContainerView testContainerView =
382            createAwTestContainerViewOnMainSync(contentsClient);
383        final AwContents awContents = testContainerView.getAwContents();
384        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
385                contentsClient.getShouldOverrideUrlLoadingHelper();
386
387        // We can't go to about:blank from here because we'd get a cross-origin error.
388        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
389                getHtmlForPageWithSimpleLinkTo(DATA_URL), "text/html", false);
390
391        int callCount = shouldOverrideUrlLoadingHelper.getCallCount();
392
393        clickOnLinkUsingJs(awContents, contentsClient);
394
395        shouldOverrideUrlLoadingHelper.waitForCallback(callCount);
396    }
397
398
399    @SmallTest
400    @Feature({"AndroidWebView", "Navigation"})
401    public void testCalledWhenSelfLinkClicked() throws Throwable {
402        final TestAwContentsClient contentsClient = new TestAwContentsClient();
403        final AwTestContainerView testContainerView =
404            createAwTestContainerViewOnMainSync(contentsClient);
405        final AwContents awContents = testContainerView.getAwContents();
406        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
407                contentsClient.getShouldOverrideUrlLoadingHelper();
408
409        final String httpPath = "/page_with_link_to_self.html";
410        final String httpPathOnServer = mWebServer.getResponseUrl(httpPath);
411        addPageToTestServer(mWebServer, httpPath,
412                getHtmlForPageWithSimpleLinkTo(httpPathOnServer));
413
414        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(),
415                httpPathOnServer);
416
417        int callCount = shouldOverrideUrlLoadingHelper.getCallCount();
418
419        clickOnLinkUsingJs(awContents, contentsClient);
420
421        shouldOverrideUrlLoadingHelper.waitForCallback(callCount);
422        assertEquals(httpPathOnServer,
423                shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
424    }
425
426    @SmallTest
427    @Feature({"AndroidWebView", "Navigation"})
428    public void testCalledWhenNavigatingFromJavaScriptUsingAssign()
429            throws Throwable {
430        final TestAwContentsClient contentsClient = new TestAwContentsClient();
431        final AwTestContainerView testContainerView =
432            createAwTestContainerViewOnMainSync(contentsClient);
433        final AwContents awContents = testContainerView.getAwContents();
434        enableJavaScriptOnUiThread(awContents);
435        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
436                contentsClient.getShouldOverrideUrlLoadingHelper();
437
438        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
439        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
440                getHtmlForPageWithJsAssignLinkTo(redirectTargetUrl), "text/html", false);
441
442        int callCount = shouldOverrideUrlLoadingHelper.getCallCount();
443
444        clickOnLinkUsingJs(awContents, contentsClient);
445
446        shouldOverrideUrlLoadingHelper.waitForCallback(callCount);
447    }
448
449    @SmallTest
450    @Feature({"AndroidWebView", "Navigation"})
451    public void testCalledWhenNavigatingFromJavaScriptUsingReplace()
452            throws Throwable {
453        final TestAwContentsClient contentsClient = new TestAwContentsClient();
454        final AwTestContainerView testContainerView =
455            createAwTestContainerViewOnMainSync(contentsClient);
456        final AwContents awContents = testContainerView.getAwContents();
457        enableJavaScriptOnUiThread(awContents);
458        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
459                contentsClient.getShouldOverrideUrlLoadingHelper();
460
461        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
462        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
463                getHtmlForPageWithJsReplaceLinkTo(redirectTargetUrl), "text/html", false);
464
465        int callCount = shouldOverrideUrlLoadingHelper.getCallCount();
466        clickOnLinkUsingJs(awContents, contentsClient);
467        shouldOverrideUrlLoadingHelper.waitForCallback(callCount);
468    }
469
470    @SmallTest
471    @Feature({"AndroidWebView", "Navigation"})
472    public void testPassesCorrectUrl() throws Throwable {
473        final TestAwContentsClient contentsClient = new TestAwContentsClient();
474        final AwTestContainerView testContainerView =
475            createAwTestContainerViewOnMainSync(contentsClient);
476        final AwContents awContents = testContainerView.getAwContents();
477        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
478                contentsClient.getShouldOverrideUrlLoadingHelper();
479
480        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
481        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
482                getHtmlForPageWithSimpleLinkTo(redirectTargetUrl), "text/html", false);
483
484        int callCount = shouldOverrideUrlLoadingHelper.getCallCount();
485        clickOnLinkUsingJs(awContents, contentsClient);
486        shouldOverrideUrlLoadingHelper.waitForCallback(callCount);
487        assertEquals(redirectTargetUrl,
488                shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
489    }
490
491    @SmallTest
492    @Feature({"AndroidWebView", "Navigation"})
493    public void testCanIgnoreLoading() throws Throwable {
494        final TestAwContentsClient contentsClient = new TestAwContentsClient();
495        final AwTestContainerView testContainerView =
496            createAwTestContainerViewOnMainSync(contentsClient);
497        final AwContents awContents = testContainerView.getAwContents();
498        final TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
499                contentsClient.getShouldOverrideUrlLoadingHelper();
500
501        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
502        final String pageWithLinkToIgnorePath = "/page_with_link_to_ignore.html";
503        final String pageWithLinkToIgnoreUrl = addPageToTestServer(mWebServer,
504                pageWithLinkToIgnorePath,
505                getHtmlForPageWithSimpleLinkTo(redirectTargetUrl));
506        final String synchronizationPath = "/sync.html";
507        final String synchronizationUrl = addPageToTestServer(mWebServer,
508                synchronizationPath,
509                getHtmlForPageWithSimpleLinkTo(redirectTargetUrl));
510
511        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(),
512                pageWithLinkToIgnoreUrl);
513
514        setShouldOverrideUrlLoadingReturnValueOnUiThread(shouldOverrideUrlLoadingHelper, true);
515
516        int callCount = shouldOverrideUrlLoadingHelper.getCallCount();
517        clickOnLinkUsingJs(awContents, contentsClient);
518        // Some time around here true should be returned from the shouldOverrideUrlLoading
519        // callback causing the navigation caused by calling clickOnLinkUsingJs to be ignored.
520        // We validate this by checking which pages were loaded on the server.
521        shouldOverrideUrlLoadingHelper.waitForCallback(callCount);
522
523        setShouldOverrideUrlLoadingReturnValueOnUiThread(shouldOverrideUrlLoadingHelper, false);
524
525        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), synchronizationUrl);
526
527        assertEquals(1, mWebServer.getRequestCount(pageWithLinkToIgnorePath));
528        assertEquals(1, mWebServer.getRequestCount(synchronizationPath));
529        assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
530    }
531
532    @SmallTest
533    @Feature({"AndroidWebView", "Navigation"})
534    public void testCalledForDataUrl() throws Throwable {
535        final String dataUrl =
536                "data:text/html;base64," +
537                "PGh0bWw+PGhlYWQ+PHRpdGxlPmRhdGFVcmxUZXN0QmFzZTY0PC90aXRsZT48" +
538                "L2hlYWQ+PC9odG1sPg==";
539        final TestAwContentsClient contentsClient = new TestAwContentsClient();
540        final AwTestContainerView testContainerView =
541            createAwTestContainerViewOnMainSync(contentsClient);
542        final AwContents awContents = testContainerView.getAwContents();
543        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
544                contentsClient.getShouldOverrideUrlLoadingHelper();
545        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
546                getHtmlForPageWithSimpleLinkTo(dataUrl), "text/html", false);
547
548        int callCount = shouldOverrideUrlLoadingHelper.getCallCount();
549        clickOnLinkUsingJs(awContents, contentsClient);
550
551        shouldOverrideUrlLoadingHelper.waitForCallback(callCount);
552        assertTrue("Expected URL that starts with 'data:' but got: <" +
553                   shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl() + "> instead.",
554                   shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl().startsWith(
555                           "data:"));
556    }
557
558    @SmallTest
559    @Feature({"AndroidWebView", "Navigation"})
560    public void testCalledForUnsupportedSchemes() throws Throwable {
561        final TestAwContentsClient contentsClient = new TestAwContentsClient();
562        final AwTestContainerView testContainerView =
563            createAwTestContainerViewOnMainSync(contentsClient);
564        final AwContents awContents = testContainerView.getAwContents();
565        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
566                contentsClient.getShouldOverrideUrlLoadingHelper();
567        final String unsupportedSchemeUrl = "foobar://resource/1";
568        loadDataSync(awContents, contentsClient.getOnPageFinishedHelper(),
569                getHtmlForPageWithSimpleLinkTo(unsupportedSchemeUrl), "text/html", false);
570
571        int callCount = shouldOverrideUrlLoadingHelper.getCallCount();
572        clickOnLinkUsingJs(awContents, contentsClient);
573
574        shouldOverrideUrlLoadingHelper.waitForCallback(callCount);
575        assertEquals(unsupportedSchemeUrl,
576                shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
577    }
578
579    @SmallTest
580    @Feature({"AndroidWebView", "Navigation"})
581    public void testNotCalledForPostNavigations() throws Throwable {
582        // The reason POST requests are excluded is BUG 155250.
583        final TestAwContentsClient contentsClient = new TestAwContentsClient();
584        final AwTestContainerView testContainerView =
585            createAwTestContainerViewOnMainSync(contentsClient);
586        final AwContents awContents = testContainerView.getAwContents();
587        final TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
588            contentsClient.getShouldOverrideUrlLoadingHelper();
589
590        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
591        final String postLinkUrl = addPageToTestServer(mWebServer, "/page_with_post_link.html",
592                getHtmlForPageWithSimplePostFormTo(redirectTargetUrl));
593
594        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), postLinkUrl);
595
596        final int shouldOverrideUrlLoadingCallCount =
597            shouldOverrideUrlLoadingHelper.getCallCount();
598
599        assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
600        clickOnLinkUsingJs(awContents, contentsClient);
601
602        // Wait for the target URL to be fetched from the server.
603        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
604            @Override
605            public boolean isSatisfied() {
606                return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
607            }
608        }, WAIT_TIMEOUT_SECONDS * 1000L, CHECK_INTERVAL));
609
610        // Since the targetURL was loaded from the test server it means all processing related
611        // to dispatching a shouldOverrideUrlLoading callback had finished and checking the call
612        // is stable.
613        assertEquals(shouldOverrideUrlLoadingCallCount,
614                shouldOverrideUrlLoadingHelper.getCallCount());
615    }
616
617    @SmallTest
618    @Feature({"AndroidWebView", "Navigation"})
619    public void testCalledFor302AfterPostNavigations() throws Throwable {
620        // The reason POST requests are excluded is BUG 155250.
621        final TestAwContentsClient contentsClient = new TestAwContentsClient();
622        final AwTestContainerView testContainerView =
623            createAwTestContainerViewOnMainSync(contentsClient);
624        final AwContents awContents = testContainerView.getAwContents();
625        final TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
626            contentsClient.getShouldOverrideUrlLoadingHelper();
627
628        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
629        final String postToGetRedirectUrl = mWebServer.setRedirect("/302.html", redirectTargetUrl);
630        final String postLinkUrl = addPageToTestServer(mWebServer, "/page_with_post_link.html",
631                getHtmlForPageWithSimplePostFormTo(postToGetRedirectUrl));
632
633        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), postLinkUrl);
634
635        final int shouldOverrideUrlLoadingCallCount =
636            shouldOverrideUrlLoadingHelper.getCallCount();
637
638        clickOnLinkUsingJs(awContents, contentsClient);
639
640        shouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
641
642        // Wait for the target URL to be fetched from the server.
643        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
644            @Override
645            public boolean isSatisfied() {
646                return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
647            }
648        }, WAIT_TIMEOUT_SECONDS * 1000L, CHECK_INTERVAL));
649
650        assertEquals(redirectTargetUrl,
651                shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
652    }
653
654    @SmallTest
655    @Feature({"AndroidWebView", "Navigation"})
656    public void testNotCalledForIframeHttpNavigations() throws Throwable {
657        final TestAwContentsClient contentsClient = new TestAwContentsClient();
658        final AwTestContainerView testContainerView =
659            createAwTestContainerViewOnMainSync(contentsClient);
660        final AwContents awContents = testContainerView.getAwContents();
661        final TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
662            contentsClient.getShouldOverrideUrlLoadingHelper();
663
664        final String iframeRedirectTargetUrl = createRedirectTargetPage(mWebServer);
665        final String iframeRedirectUrl =
666            mWebServer.setRedirect("/302.html", iframeRedirectTargetUrl);
667        final String pageWithIframeUrl =
668            addPageToTestServer(mWebServer, "/iframe_intercept.html",
669                makeHtmlPageFrom("", "<iframe src=\"" + iframeRedirectUrl + "\" />"));
670
671        final int shouldOverrideUrlLoadingCallCount =
672            shouldOverrideUrlLoadingHelper.getCallCount();
673
674        assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
675        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), pageWithIframeUrl);
676
677        // Wait for the redirect target URL to be fetched from the server.
678        assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
679            @Override
680            public boolean isSatisfied() {
681                return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
682            }
683        }, WAIT_TIMEOUT_SECONDS * 1000L, CHECK_INTERVAL));
684
685        assertEquals(shouldOverrideUrlLoadingCallCount,
686                shouldOverrideUrlLoadingHelper.getCallCount());
687    }
688
689    @SmallTest
690    @Feature({"AndroidWebView", "Navigation"})
691    public void testCalledForIframeUnsupportedSchemeNavigations() throws Throwable {
692        final TestAwContentsClient contentsClient = new TestAwContentsClient();
693        final AwTestContainerView testContainerView =
694            createAwTestContainerViewOnMainSync(contentsClient);
695        final AwContents awContents = testContainerView.getAwContents();
696        final TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
697            contentsClient.getShouldOverrideUrlLoadingHelper();
698
699        final String unsupportedSchemeUrl = "foobar://resource/1";
700        final String pageWithIframeUrl =
701            addPageToTestServer(mWebServer, "/iframe_intercept.html",
702                makeHtmlPageFrom("", "<iframe src=\"" + unsupportedSchemeUrl + "\" />"));
703
704        final int shouldOverrideUrlLoadingCallCount =
705            shouldOverrideUrlLoadingHelper.getCallCount();
706
707        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), pageWithIframeUrl);
708
709        shouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
710        assertEquals(unsupportedSchemeUrl,
711                shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
712    }
713
714    /**
715     * Worker method for the various redirect tests.
716     *
717     * Calling this will first load the redirect URL built from redirectFilePath, query and
718     * locationFilePath and assert that we get a override callback for the destination.
719     * The second part of the test loads a page that contains a link which points at the redirect
720     * URL. We expect two callbacks - one for the redirect link and another for the destination.
721     */
722    private void doTestCalledOnRedirect(TestWebServer webServer,
723            String redirectUrl, String redirectTarget) throws Throwable {
724        final TestAwContentsClient contentsClient = new TestAwContentsClient();
725        final AwTestContainerView testContainerView =
726            createAwTestContainerViewOnMainSync(contentsClient);
727        final AwContents awContents = testContainerView.getAwContents();
728        final String pageWithLinkToRedirectUrl = addPageToTestServer(webServer,
729                "/page_with_link_to_redirect.html",
730                getHtmlForPageWithSimpleLinkTo(redirectUrl));
731        enableJavaScriptOnUiThread(awContents);
732
733        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
734                contentsClient.getShouldOverrideUrlLoadingHelper();
735        int directLoadCallCount = shouldOverrideUrlLoadingHelper.getCallCount();
736        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(), redirectUrl);
737
738        shouldOverrideUrlLoadingHelper.waitForCallback(directLoadCallCount, 1);
739        assertEquals(redirectTarget,
740                shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
741
742        // There is a slight difference between navigations caused by calling load and navigations
743        // caused by clicking on a link:
744        //  * when using load the navigation is treated as if it came from the URL bar (has the
745        //    navigation type TYPED and doesn't have the has_user_gesture flag)
746        //  * when clicking on a link the navigation has the LINK type and has_user_gesture is
747        //    true.
748        // Both of these should yield the same result which is what we're verifying here.
749        int indirectLoadCallCount = shouldOverrideUrlLoadingHelper.getCallCount();
750        loadUrlSync(awContents, contentsClient.getOnPageFinishedHelper(),
751                pageWithLinkToRedirectUrl);
752
753        assertEquals(indirectLoadCallCount, shouldOverrideUrlLoadingHelper.getCallCount());
754
755        clickOnLinkUsingJs(awContents, contentsClient);
756
757        shouldOverrideUrlLoadingHelper.waitForCallback(indirectLoadCallCount, 2);
758        assertEquals(redirectTarget,
759                shouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
760        assertEquals(redirectUrl,
761                shouldOverrideUrlLoadingHelper.getPreviousShouldOverrideUrlLoadingUrl());
762    }
763
764    @SmallTest
765    @Feature({"AndroidWebView", "Navigation"})
766    public void testCalledOn302Redirect() throws Throwable {
767        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
768        final String redirectUrl = mWebServer.setRedirect("/302.html", redirectTargetUrl);
769
770        doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
771    }
772
773    @SmallTest
774    @Feature({"AndroidWebView", "Navigation"})
775    public void testCalledOnMetaRefreshRedirect() throws Throwable {
776        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
777        final String redirectUrl = addPageToTestServer(mWebServer, "/meta_refresh.html",
778                getHtmlForPageWithMetaRefreshRedirectTo(redirectTargetUrl));
779        doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
780    }
781
782
783    @SmallTest
784    @Feature({"AndroidWebView", "Navigation"})
785    public void testCalledOnJavaScriptLocationImmediateAssignRedirect()
786            throws Throwable {
787        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
788        final String redirectUrl = addPageToTestServer(mWebServer, "/js_immediate_assign.html",
789                getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Assign", 0));
790        doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
791    }
792
793    @SmallTest
794    @Feature({"AndroidWebView", "Navigation"})
795    public void testCalledOnJavaScriptLocationImmediateReplaceRedirect()
796            throws Throwable {
797        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
798        final String redirectUrl = addPageToTestServer(mWebServer, "/js_immediate_replace.html",
799                getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Replace", 0));
800        doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
801    }
802
803    @SmallTest
804    @Feature({"AndroidWebView", "Navigation"})
805    public void testCalledOnJavaScriptLocationDelayedAssignRedirect()
806            throws Throwable {
807        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
808        final String redirectUrl = addPageToTestServer(mWebServer, "/js_delayed_assign.html",
809                getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Assign", 100));
810        doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
811    }
812
813    @SmallTest
814    @Feature({"AndroidWebView", "Navigation"})
815    public void testCalledOnJavaScriptLocationDelayedReplaceRedirect()
816            throws Throwable {
817        final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
818        final String redirectUrl = addPageToTestServer(mWebServer, "/js_delayed_replace.html",
819                getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Replace", 100));
820        doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
821    }
822
823    @SmallTest
824    @Feature({"AndroidWebView", "Navigation"})
825    public void testDoubleNavigateDoesNotSuppressInitialNavigate() throws Throwable {
826        final String jsUrl = "javascript:try{console.log('processed js loadUrl');}catch(e){};";
827        final TestAwContentsClient contentsClient = new TestAwContentsClient();
828        final AwTestContainerView testContainerView =
829            createAwTestContainerViewOnMainSync(contentsClient);
830        final AwContents awContents = testContainerView.getAwContents();
831        TestAwContentsClient.ShouldOverrideUrlLoadingHelper shouldOverrideUrlLoadingHelper =
832            contentsClient.getShouldOverrideUrlLoadingHelper();
833
834        // Do a double navigagtion, the second being an effective no-op, in quick succession (i.e.
835        // without yielding the main thread inbetween).
836        int currentCallCount = contentsClient.getOnPageFinishedHelper().getCallCount();
837        getInstrumentation().runOnMainSync(new Runnable() {
838            @Override
839            public void run() {
840                awContents.loadUrl(LoadUrlParams.createLoadDataParams(
841                        getHtmlForPageWithSimpleLinkTo(DATA_URL), "text/html", false));
842                awContents.loadUrl(new LoadUrlParams(jsUrl));
843            }
844        });
845        contentsClient.getOnPageFinishedHelper().waitForCallback(currentCallCount, 1,
846                WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
847
848        assertEquals(0, shouldOverrideUrlLoadingHelper.getCallCount());
849    }
850}
851