1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2014 Intel Corporation. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
6 package org.xwalk.core.xwview.test;
8 import android.test.suitebuilder.annotation.SmallTest;
9 import android.util.Pair;
11 import java.util.ArrayList;
12 import java.util.concurrent.Callable;
13 import java.util.concurrent.TimeUnit;
14 import java.util.List;
16 import org.chromium.base.test.util.DisabledTest;
17 import org.chromium.base.test.util.Feature;
18 import org.chromium.content.browser.LoadUrlParams;
19 import org.chromium.content.browser.test.util.CallbackHelper;
20 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageStartedHelper;
21 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnReceivedErrorHelper;
22 import org.chromium.net.test.util.TestWebServer;
24 import org.xwalk.core.xwview.test.util.CommonResources;
27 * Tests for the shouldOverrideUrlLoading() method.
29 public class ShouldOverrideUrlLoadingTest extends XWalkViewTestBase {
30 private static final String ABOUT_BLANK_URL = "about:blank";
31 private static final String DATA_URL = "data:text/html,<div/>";
32 private static final String REDIRECT_TARGET_PATH = "/redirect_target.html";
33 private static final String TITLE = "TITLE";
34 private TestHelperBridge.ShouldOverrideUrlLoadingHelper mShouldOverrideUrlLoadingHelper;
35 private TestWebServer mWebServer;
38 protected void setUp() throws Exception {
40 mShouldOverrideUrlLoadingHelper = mTestHelperBridge.getShouldOverrideUrlLoadingHelper();
41 mWebServer = new TestWebServer(false);
45 protected void tearDown() throws Exception {
46 mWebServer.shutdown();
50 // Since this value is read on the UI thread, it's simpler to set it there too.
51 void setShouldOverrideUrlLoadingReturnValueOnUiThread(
52 final boolean value) throws Throwable {
53 runTestOnUiThread(new Runnable() {
56 mShouldOverrideUrlLoadingHelper.setShouldOverrideUrlLoadingReturnValue(value);
61 private String getTestPageCommonHeaders() {
62 return "<title>" + TITLE + "</title> ";
65 private String makeHtmlPageFrom(String headers, String body) {
66 return CommonResources.makeHtmlPageFrom(getTestPageCommonHeaders() + headers, body);
69 private String getHtmlForPageWithJsAssignLinkTo(String url) {
70 return makeHtmlPageFrom("",
71 "<img onclick=\"location.href='" + url + "'\" class=\"big\" id=\"link\" />");
74 private String getHtmlForPageWithJsReplaceLinkTo(String url) {
75 return makeHtmlPageFrom("",
76 "<img onclick=\"location.replace('" + url + "');\" class=\"big\" id=\"link\" />");
79 private String getHtmlForPageWithMetaRefreshRedirectTo(String url) {
80 return makeHtmlPageFrom("<meta http-equiv=\"refresh\" content=\"0;url=" + url + "\" />",
81 "<div>Meta refresh redirect</div>");
84 private String getHtmlForPageWithJsRedirectTo(String url, String method, int timeout) {
85 return makeHtmlPageFrom(
87 "function doRedirectAssign() {" +
88 "location.href = '" + url + "';" +
90 "function doRedirectReplace() {" +
91 "location.replace('" + url + "');" +
94 String.format("<iframe onLoad=\"setTimeout('doRedirect%s()', %d);\" />",
98 private String addPageToTestServer(TestWebServer webServer, String httpPath, String html) {
99 List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
100 headers.add(Pair.create("Content-Type", "text/html"));
101 headers.add(Pair.create("Cache-Control", "no-store"));
102 return webServer.setResponse(httpPath, html, headers);
105 private String createRedirectTargetPage(TestWebServer webServer) {
106 return addPageToTestServer(webServer, REDIRECT_TARGET_PATH,
107 makeHtmlPageFrom("", "<div>This is the end of the redirect chain</div>"));
111 @Feature({"XWalkView", "Navigation"})
112 public void testNotCalledOnLoadData() throws Throwable {
114 CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
116 assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());
119 private void waitForNavigationRunnableAndAssertTitleChanged(
120 CallbackHelper onPageFinishedHelper,
121 Runnable navigationRunnable) throws Exception {
122 final int callCount = onPageFinishedHelper.getCallCount();
123 final String oldTitle = getTitleOnUiThread();
124 getInstrumentation().runOnMainSync(navigationRunnable);
125 onPageFinishedHelper.waitForCallback(callCount);
126 assertFalse(oldTitle.equals(getTitleOnUiThread()));
130 @Feature({"XWalkView", "Navigation"})
131 public void testCalledOnBackForwardNavigation() throws Throwable {
132 final String url = "file:///android_asset/www/index.html";
133 final String httpPath = "/test.html";
134 final String httpPathOnServer = mWebServer.getResponseUrl(httpPath);
135 addPageToTestServer(mWebServer, httpPath,
136 CommonResources.makeHtmlPageWithSimpleLinkTo(httpPathOnServer));
138 loadUrlSync(httpPathOnServer);
140 assertEquals(2, mShouldOverrideUrlLoadingHelper.getCallCount());
141 String oldTitle = getTitleOnUiThread();
143 assertFalse(oldTitle.equals(getTitleOnUiThread()));
144 assertEquals(3, mShouldOverrideUrlLoadingHelper.getCallCount());
146 oldTitle = getTitleOnUiThread();
148 assertFalse(oldTitle.equals(getTitleOnUiThread()));
149 assertEquals(4, mShouldOverrideUrlLoadingHelper.getCallCount());
153 @Feature({"XWalkView", "Navigation"})
154 public void testCantBlockLoads() throws Throwable {
155 setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
158 CommonResources.makeHtmlPageWithSimpleLinkTo(getTestPageCommonHeaders(),
159 DATA_URL), "text/html", false);
161 assertEquals(TITLE, getTitleOnUiThread());
165 @Feature({"XWalkView", "Navigation"})
166 public void testCalledBeforeOnPageStarted() throws Throwable {
167 OnPageStartedHelper onPageStartedHelper = mTestHelperBridge.getOnPageStartedHelper();
170 CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
172 final int shouldOverrideUrlLoadingCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
173 final int onPageStartedCallCount = onPageStartedHelper.getCallCount();
174 setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
175 clickOnElementId("link");
177 mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
178 assertEquals(onPageStartedCallCount, onPageStartedHelper.getCallCount());
182 @Feature({"XWalkView", "Navigation"})
183 public void testDoesNotCauseOnReceivedError() throws Throwable {
184 OnReceivedErrorHelper onReceivedErrorHelper = mTestHelperBridge.getOnReceivedErrorHelper();
185 final int onReceivedErrorCallCount = onReceivedErrorHelper.getCallCount();
188 CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
190 final int shouldOverrideUrlLoadingCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
192 setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
194 clickOnElementId("link");
196 mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
198 setShouldOverrideUrlLoadingReturnValueOnUiThread(false);
200 // After we load this URL we're certain that any in-flight callbacks for the previous
201 // navigation have been delivered.
202 loadUrlSync(ABOUT_BLANK_URL);
204 assertEquals(onReceivedErrorCallCount, onReceivedErrorHelper.getCallCount());
208 @Feature({"XWalkView", "Navigation"})
209 public void testNotCalledForAnchorNavigations() throws Throwable {
210 doTestNotCalledForAnchorNavigations(false);
214 @Feature({"XWalkView", "Navigation"})
215 public void testNotCalledForAnchorNavigationsWithNonHierarchicalScheme() throws Throwable {
216 doTestNotCalledForAnchorNavigations(true);
219 private void doTestNotCalledForAnchorNavigations(boolean useLoadData) throws Throwable {
220 final String anchorLinkPath = "/anchor_link.html";
221 final String anchorLinkUrl = mWebServer.getResponseUrl(anchorLinkPath);
222 addPageToTestServer(mWebServer, anchorLinkPath,
223 CommonResources.makeHtmlPageWithSimpleLinkTo(anchorLinkUrl + "#anchor"));
227 CommonResources.makeHtmlPageWithSimpleLinkTo("#anchor"), "text/html", false);
229 loadUrlSync(anchorLinkUrl);
232 final int shouldOverrideUrlLoadingCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
234 clickOnElementId("link");
236 // After we load this URL we're certain that any in-flight callbacks for the previous
237 // navigation have been delivered.
238 loadUrlSync(ABOUT_BLANK_URL);
240 assertEquals(shouldOverrideUrlLoadingCallCount,
241 mShouldOverrideUrlLoadingHelper.getCallCount());
245 @Feature({"XWalkView", "Navigation"})
246 public void testCalledWhenLinkClicked() throws Throwable {
247 // We can't go to about:blank from here because we'd get a cross-origin error.
249 CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
251 int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
253 clickOnElementId("link");
255 mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
259 @Feature({"XWalkView", "Navigation"})
260 public void testCalledWhenSelfLinkClicked() throws Throwable {
261 final String httpPath = "/page_with_link_to_self.html";
262 final String httpPathOnServer = mWebServer.getResponseUrl(httpPath);
263 addPageToTestServer(mWebServer, httpPath,
264 CommonResources.makeHtmlPageWithSimpleLinkTo(httpPathOnServer));
266 loadUrlSync(httpPathOnServer);
268 int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
270 clickOnElementId("link");
272 mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
273 assertEquals(httpPathOnServer,
274 mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
278 @Feature({"XWalkView", "Navigation"})
279 public void testCalledWhenNavigatingFromJavaScriptUsingAssign()
281 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
283 getHtmlForPageWithJsAssignLinkTo(redirectTargetUrl), "text/html", false);
285 int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
287 clickOnElementId("link");
289 mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
293 @Feature({"XWalkView", "Navigation"})
294 public void testCalledWhenNavigatingFromJavaScriptUsingReplace()
296 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
298 getHtmlForPageWithJsReplaceLinkTo(redirectTargetUrl), "text/html", false);
300 int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
301 clickOnElementId("link");
302 mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
306 @Feature({"XWalkView", "Navigation"})
307 public void testPassesCorrectUrl() throws Throwable {
308 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
310 CommonResources.makeHtmlPageWithSimpleLinkTo(redirectTargetUrl), "text/html", false);
312 int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
313 clickOnElementId("link");
314 mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
315 assertEquals(redirectTargetUrl,
316 mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
319 // This test case is not stable, disable it temporarily.
321 // @Feature({"XWalkView", "Navigation"})
323 public void testCanIgnoreLoading() throws Throwable {
324 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
325 final String pageWithLinkToIgnorePath = "/page_with_link_to_ignore.html";
326 final String pageWithLinkToIgnoreUrl = addPageToTestServer(mWebServer,
327 pageWithLinkToIgnorePath, CommonResources.makeHtmlPageWithSimpleLinkTo(redirectTargetUrl));
328 final String synchronizationPath = "/sync.html";
329 final String synchronizationUrl = addPageToTestServer(mWebServer, synchronizationPath,
330 CommonResources.makeHtmlPageWithSimpleLinkTo(redirectTargetUrl));
332 loadUrlSync(pageWithLinkToIgnoreUrl);
334 setShouldOverrideUrlLoadingReturnValueOnUiThread(true);
336 int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
337 clickOnElementId("link");
338 // Some time around here true should be returned from the shouldOverrideUrlLoading
339 // callback causing the navigation caused by calling clickOnElementId to be ignored.
340 // We validate this by checking which pages were loaded on the server.
341 mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
343 setShouldOverrideUrlLoadingReturnValueOnUiThread(false);
345 loadUrlSync(synchronizationUrl);
347 assertEquals(1, mWebServer.getRequestCount(pageWithLinkToIgnorePath));
348 assertEquals(1, mWebServer.getRequestCount(synchronizationPath));
349 assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
353 @Feature({"XWalkView", "Navigation"})
354 public void testCalledForDataUrl() throws Throwable {
355 final String dataUrl =
356 "data:text/html;base64," +
357 "PGh0bWw+PGhlYWQ+PHRpdGxlPmRhdGFVcmxUZXN0QmFzZTY0PC90aXRsZT48" +
358 "L2hlYWQ+PC9odG1sPg==";
360 CommonResources.makeHtmlPageWithSimpleLinkTo(dataUrl), "text/html", false);
362 int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
363 clickOnElementId("link");
365 mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
366 assertTrue("Expected URL that starts with 'data:' but got: <" +
367 mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl() + "> instead.",
368 mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl().startsWith(
373 @Feature({"XWalkView", "Navigation"})
374 public void testCalledForUnsupportedSchemes() throws Throwable {
375 final String unsupportedSchemeUrl = "foobar://resource/1";
377 CommonResources.makeHtmlPageWithSimpleLinkTo(unsupportedSchemeUrl), "text/html",
380 int callCount = mShouldOverrideUrlLoadingHelper.getCallCount();
381 clickOnElementId("link");
383 mShouldOverrideUrlLoadingHelper.waitForCallback(callCount);
384 assertEquals(unsupportedSchemeUrl,
385 mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
389 @Feature({"XWalkView", "Navigation"})
390 public void testCalledForPostNavigations() throws Throwable {
391 // The reason POST requests are excluded is BUG 155250.
392 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
393 final String postLinkUrl = addPageToTestServer(mWebServer, "/page_with_post_link.html",
394 CommonResources.makeHtmlPageWithSimplePostFormTo(redirectTargetUrl));
396 loadUrlSync(postLinkUrl);
398 final int shouldOverrideUrlLoadingCallCount =
399 mShouldOverrideUrlLoadingHelper.getCallCount();
401 assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
402 clickOnElementId("link");
404 // Wait for the target URL to be fetched from the server.
405 pollOnUiThread(new Callable<Boolean>() {
407 public Boolean call() throws Exception {
408 return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
412 // Since the targetURL was loaded from the test server it means all processing related
413 // to dispatching a shouldOverrideUrlLoading callback had finished and checking the call
415 assertEquals(shouldOverrideUrlLoadingCallCount + 1,
416 mShouldOverrideUrlLoadingHelper.getCallCount());
420 @Feature({"XWalkView", "Navigation"})
421 public void testCalledFor302AfterPostNavigations() throws Throwable {
422 // The reason POST requests are excluded is BUG 155250.
423 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
424 final String postToGetRedirectUrl = mWebServer.setRedirect("/302.html", redirectTargetUrl);
425 final String postLinkUrl = addPageToTestServer(mWebServer, "/page_with_post_link.html",
426 CommonResources.makeHtmlPageWithSimplePostFormTo(postToGetRedirectUrl));
428 loadUrlSync(postLinkUrl);
430 final int shouldOverrideUrlLoadingCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
432 clickOnElementId("link");
434 mShouldOverrideUrlLoadingHelper.waitForCallback(shouldOverrideUrlLoadingCallCount);
436 // Wait for the target URL to be fetched from the server.
437 pollOnUiThread(new Callable<Boolean>() {
439 public Boolean call() throws Exception {
440 return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
444 assertEquals(redirectTargetUrl,
445 mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
449 @Feature({"XWalkView", "Navigation"})
450 public void testCalledForIframeHttpNavigations() throws Throwable {
451 final String iframeRedirectTargetUrl = createRedirectTargetPage(mWebServer);
452 final String iframeRedirectUrl = mWebServer.setRedirect("/302.html", iframeRedirectTargetUrl);
453 final String pageWithIframeUrl = addPageToTestServer(mWebServer, "/iframe_intercept.html",
454 makeHtmlPageFrom("", "<iframe src=\"" + iframeRedirectUrl + "\" />"));
456 assertEquals(0, mWebServer.getRequestCount(REDIRECT_TARGET_PATH));
457 loadUrlSync(pageWithIframeUrl);
459 // Wait for the redirect target URL to be fetched from the server.
460 pollOnUiThread(new Callable<Boolean>() {
462 public Boolean call() throws Exception {
463 return mWebServer.getRequestCount(REDIRECT_TARGET_PATH) == 1;
467 assertEquals(1, mShouldOverrideUrlLoadingHelper.getCallCount());
471 * Worker method for the various redirect tests.
473 * Calling this will first load the redirect URL built from redirectFilePath, query and
474 * locationFilePath and assert that we get a override callback for the destination.
475 * The second part of the test loads a page that contains a link which points at the redirect
476 * URL. We expect two callbacks - one for the redirect link and another for the destination.
478 private void doTestCalledOnRedirect(TestWebServer webServer,
479 String redirectUrl, String redirectTarget) throws Throwable {
480 final String pageWithLinkToRedirectUrl = addPageToTestServer(webServer,
481 "/page_with_link_to_redirect.html",
482 CommonResources.makeHtmlPageWithSimpleLinkTo(redirectUrl));
483 int directLoadCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
484 loadUrlSync(redirectUrl);
486 mShouldOverrideUrlLoadingHelper.waitForCallback(directLoadCallCount, 2);
487 assertEquals(redirectTarget,
488 mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
490 int indirectLoadCallCount = mShouldOverrideUrlLoadingHelper.getCallCount();
491 loadUrlSync(pageWithLinkToRedirectUrl);
493 clickOnElementId("link");
495 mShouldOverrideUrlLoadingHelper.waitForCallback(indirectLoadCallCount, 3);
496 assertEquals(redirectTarget,
497 mShouldOverrideUrlLoadingHelper.getShouldOverrideUrlLoadingUrl());
498 assertEquals(redirectUrl,
499 mShouldOverrideUrlLoadingHelper.getPreviousShouldOverrideUrlLoadingUrl());
503 @Feature({"XWalkView", "Navigation"})
504 public void testCalledOn302Redirect() throws Throwable {
505 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
506 final String redirectUrl = mWebServer.setRedirect("/302.html", redirectTargetUrl);
508 doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
512 @Feature({"XWalkView", "Navigation"})
513 public void testCalledOnMetaRefreshRedirect() throws Throwable {
514 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
515 final String redirectUrl = addPageToTestServer(mWebServer, "/meta_refresh.html",
516 getHtmlForPageWithMetaRefreshRedirectTo(redirectTargetUrl));
517 doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
522 @Feature({"XWalkView", "Navigation"})
523 public void testCalledOnJavaScriptLocationImmediateAssignRedirect()
525 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
526 final String redirectUrl = addPageToTestServer(mWebServer, "/js_immediate_assign.html",
527 getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Assign", 0));
528 doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
532 @Feature({"XWalkView", "Navigation"})
533 public void testCalledOnJavaScriptLocationImmediateReplaceRedirect()
535 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
536 final String redirectUrl = addPageToTestServer(mWebServer, "/js_immediate_replace.html",
537 getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Replace", 0));
538 doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
542 @Feature({"XWalkView", "Navigation"})
543 public void testCalledOnJavaScriptLocationDelayedAssignRedirect()
545 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
546 final String redirectUrl = addPageToTestServer(mWebServer, "/js_delayed_assign.html",
547 getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Assign", 100));
548 doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
552 @Feature({"XWalkView", "Navigation"})
553 public void testCalledOnJavaScriptLocationDelayedReplaceRedirect()
555 final String redirectTargetUrl = createRedirectTargetPage(mWebServer);
556 final String redirectUrl = addPageToTestServer(mWebServer, "/js_delayed_replace.html",
557 getHtmlForPageWithJsRedirectTo(redirectTargetUrl, "Replace", 100));
558 doTestCalledOnRedirect(mWebServer, redirectUrl, redirectTargetUrl);
562 @Feature({"XWalkView", "Navigation"})
563 public void testDoubleNavigateDoesNotSuppressInitialNavigate() throws Throwable {
564 final String jsUrl = "javascript:try{console.log('processed js loadUrl');}catch(e){};";
566 // Do a double navigagtion, the second being an effective no-op, in quick succession (i.e.
567 // without yielding the main thread inbetween).
569 CommonResources.makeHtmlPageWithSimpleLinkTo(DATA_URL), "text/html", false);
570 loadJavaScriptUrl(jsUrl);
571 assertEquals(0, mShouldOverrideUrlLoadingHelper.getCallCount());