1 // Copyright (c) 2013 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.graphics.Bitmap;
9 import android.test.suitebuilder.annotation.SmallTest;
10 import android.util.Log;
11 import android.util.Pair;
12 import android.webkit.WebResourceResponse;
13 import org.chromium.base.test.util.DisabledTest;
14 import org.chromium.base.test.util.Feature;
15 import org.chromium.base.test.util.TestFileUtil;
16 import org.chromium.content.browser.test.util.CallbackHelper;
17 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnReceivedErrorHelper;
18 import org.chromium.net.test.util.TestWebServer;
20 import org.xwalk.core.xwview.test.util.CommonResources;
21 import org.xwalk.core.XWalkClient;
22 import org.xwalk.core.XWalkContent;
23 import org.xwalk.core.XWalkView;
25 import java.io.ByteArrayInputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.concurrent.Callable;
31 import java.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.CountDownLatch;
35 * Test case for XWalkClient.shouldInterceptRequest callback
37 * Note the major part of this file is migrated from android_webview/.
39 public class XWalkClientShouldInterceptRequestTest extends XWalkViewTestBase {
42 * Customized XWalkClient implementation for shouldInterceptRequest
44 private class TestXWalkClient extends XWalkClient {
46 public class ShouldInterceptRequestHelper extends CallbackHelper {
47 private List<String> mShouldInterceptRequestUrls = new ArrayList<String>();
48 private ConcurrentHashMap<String, WebResourceResponse> mReturnValuesByUrls
49 = new ConcurrentHashMap<String, WebResourceResponse>();
50 // This is read from the IO thread, so needs to be marked volatile.
51 private volatile WebResourceResponse mResourceResponseReturnValue = null;
52 private String mUrlToWaitFor;
54 void setReturnValue(WebResourceResponse value) {
55 mResourceResponseReturnValue = value;
58 void setReturnValueForUrl(String url, WebResourceResponse value) {
59 mReturnValuesByUrls.put(url, value);
62 public void setUrlToWaitFor(String url) {
66 public List<String> getUrls() {
67 assert getCallCount() > 0;
68 return mShouldInterceptRequestUrls;
71 public WebResourceResponse getReturnValue(String url) {
72 WebResourceResponse value = mReturnValuesByUrls.get(url);
73 if (value != null) return value;
74 return mResourceResponseReturnValue;
77 public void notifyCalled(String url) {
78 if (mUrlToWaitFor == null || mUrlToWaitFor.equals(url)) {
79 mShouldInterceptRequestUrls.add(url);
85 public class OnLoadResourceHelper extends CallbackHelper {
88 public String getUrl() {
89 assert getCallCount() > 0;
93 public void notifyCalled(String url) {
100 public void onPageStarted(XWalkView view, String url, Bitmap favicon) {
101 mTestContentsClient.onPageStarted(url);
105 public void onPageFinished(XWalkView view, String url) {
106 mTestContentsClient.didFinishLoad(url);
110 public void onReceivedError(XWalkView view, int errorCode,
111 String description, String failingUrl) {
112 mTestContentsClient.onReceivedError(errorCode, description, failingUrl);
116 public WebResourceResponse shouldInterceptRequest(XWalkView view, String url) {
117 WebResourceResponse response = mShouldInterceptRequestHelper.getReturnValue(url);
118 mShouldInterceptRequestHelper.notifyCalled(url);
123 public void onLoadResource(XWalkView view, String url) {
124 super.onLoadResource(view, url);
125 mOnLoadResourceHelper.notifyCalled(url);
128 private ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
129 private OnLoadResourceHelper mOnLoadResourceHelper;
131 public TestXWalkClient() {
132 mShouldInterceptRequestHelper = new ShouldInterceptRequestHelper();
133 mOnLoadResourceHelper = new OnLoadResourceHelper();
136 public ShouldInterceptRequestHelper getShouldInterceptRequestHelper() {
137 return mShouldInterceptRequestHelper;
140 public OnLoadResourceHelper getOnLoadResourceHelper() {
141 return mOnLoadResourceHelper;
145 private String addPageToTestServer(TestWebServer webServer, String httpPath, String html) {
146 List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
147 headers.add(Pair.create("Content-Type", "text/html"));
148 headers.add(Pair.create("Cache-Control", "no-store"));
149 return webServer.setResponse(httpPath, html, headers);
152 private String addAboutPageToTestServer(TestWebServer webServer) {
153 return addPageToTestServer(webServer, "/" + CommonResources.ABOUT_FILENAME,
154 CommonResources.ABOUT_HTML);
157 private WebResourceResponse stringToWebResourceResponse(String input) throws Throwable {
158 final String mimeType = "text/html";
159 final String encoding = "UTF-8";
161 return new WebResourceResponse(
162 mimeType, encoding, new ByteArrayInputStream(input.getBytes(encoding)));
165 private TestWebServer mWebServer;
166 private TestXWalkClient mTestXWalkClient;
167 private TestXWalkClient.ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
168 private TestXWalkClient.OnLoadResourceHelper mOnLoadResourceHelper;
171 protected void setUp() throws Exception {
174 getInstrumentation().runOnMainSync(new Runnable() {
177 mTestXWalkClient = new TestXWalkClient();
178 getXWalkView().setXWalkClient(mTestXWalkClient);
179 mShouldInterceptRequestHelper = mTestXWalkClient.getShouldInterceptRequestHelper();
180 mOnLoadResourceHelper = mTestXWalkClient.getOnLoadResourceHelper();
184 mWebServer = new TestWebServer(false);
188 protected void tearDown() throws Exception {
189 mWebServer.shutdown();
194 @Feature({"XWalkClientShouldInterceptRequest"})
195 public void testCalledWithCorrectUrl() throws Throwable {
196 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
198 int callCount = mShouldInterceptRequestHelper.getCallCount();
199 int onPageFinishedCallCount = mTestContentsClient.getOnPageFinishedHelper().getCallCount();
201 loadUrlAsync(aboutPageUrl);
203 mShouldInterceptRequestHelper.waitForCallback(callCount);
204 assertEquals(1, mShouldInterceptRequestHelper.getUrls().size());
205 assertEquals(aboutPageUrl,
206 mShouldInterceptRequestHelper.getUrls().get(0));
208 mTestContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
209 assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread());
213 @Feature({"XWalkClientShouldInterceptRequest"})
214 public void testOnLoadResourceCalledWithCorrectUrl() throws Throwable {
215 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
216 int callCount = mOnLoadResourceHelper.getCallCount();
218 loadUrlAsync(aboutPageUrl);
220 mOnLoadResourceHelper.waitForCallback(callCount);
221 assertEquals(aboutPageUrl, mOnLoadResourceHelper.getUrl());
225 @Feature({"XWalkClientShouldInterceptRequest"})
226 public void testDoesNotCrashOnInvalidData() throws Throwable {
227 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
229 mShouldInterceptRequestHelper.setReturnValue(
230 new WebResourceResponse("text/html", "UTF-8", null));
231 int callCount = mShouldInterceptRequestHelper.getCallCount();
232 loadUrlAsync(aboutPageUrl);
233 mShouldInterceptRequestHelper.waitForCallback(callCount);
235 mShouldInterceptRequestHelper.setReturnValue(
236 new WebResourceResponse(null, null, new ByteArrayInputStream(new byte[0])));
237 callCount = mShouldInterceptRequestHelper.getCallCount();
238 loadUrlAsync(aboutPageUrl);
239 mShouldInterceptRequestHelper.waitForCallback(callCount);
241 mShouldInterceptRequestHelper.setReturnValue(
242 new WebResourceResponse(null, null, null));
243 callCount = mShouldInterceptRequestHelper.getCallCount();
244 loadUrlAsync(aboutPageUrl);
245 mShouldInterceptRequestHelper.waitForCallback(callCount);
248 private static class EmptyInputStream extends InputStream {
250 public int available() {
255 public int read() throws IOException {
260 public int read(byte b[]) throws IOException {
265 public int read(byte b[], int off, int len) throws IOException {
270 public long skip(long n) throws IOException {
272 throw new IOException("skipping negative number of bytes");
278 @Feature({"XWalkClientShouldInterceptRequest"})
279 public void testDoesNotCrashOnEmptyStream() throws Throwable {
280 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
282 mShouldInterceptRequestHelper.setReturnValue(
283 new WebResourceResponse("text/html", "UTF-8", new EmptyInputStream()));
284 int shouldInterceptRequestCallCount = mShouldInterceptRequestHelper.getCallCount();
285 int onPageFinishedCallCount = mTestContentsClient.getOnPageFinishedHelper().getCallCount();
287 loadUrlAsync(aboutPageUrl);
289 mShouldInterceptRequestHelper.waitForCallback(shouldInterceptRequestCallCount);
290 mTestContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
293 private static class SlowWebResourceResponse extends WebResourceResponse {
294 private CallbackHelper mReadStartedCallbackHelper = new CallbackHelper();
295 private CountDownLatch mLatch = new CountDownLatch(1);
297 public SlowWebResourceResponse(String mimeType, String encoding, InputStream data) {
298 super(mimeType, encoding, data);
302 public InputStream getData() {
303 mReadStartedCallbackHelper.notifyCalled();
306 } catch (InterruptedException e) {
309 return super.getData();
312 public void unblockReads() {
316 public CallbackHelper getReadStartedCallbackHelper() {
317 return mReadStartedCallbackHelper;
322 @Feature({"XWalkClientShouldInterceptRequest"})
323 public void testHttpStatusField() throws Throwable {
324 final String syncGetUrl = mWebServer.getResponseUrl("/intercept_me");
325 final String syncGetJs =
327 " var xhr = new XMLHttpRequest();" +
328 " xhr.open('GET', '" + syncGetUrl + "', false);" +
330 " console.info('xhr.status = ' + xhr.status);" +
331 " return xhr.status;" +
334 getInstrumentation().runOnMainSync(new Runnable() {
337 getXWalkView().getSettings().setJavaScriptEnabled(true);
341 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
342 loadUrlSync(aboutPageUrl);
344 mShouldInterceptRequestHelper.setReturnValue(
345 new WebResourceResponse("text/html", "UTF-8", null));
346 assertEquals("404", executeJavaScriptAndWaitForResult(syncGetJs));
348 mShouldInterceptRequestHelper.setReturnValue(
349 new WebResourceResponse("text/html", "UTF-8", new EmptyInputStream()));
350 assertEquals("200", executeJavaScriptAndWaitForResult(syncGetJs));
354 private String makePageWithTitle(String title) {
355 return CommonResources.makeHtmlPageFrom("<title>" + title + "</title>",
356 "<div> The title is: " + title + " </div>");
360 @Feature({"XWalkClientShouldInterceptRequest"})
361 public void testCanInterceptMainFrame() throws Throwable {
362 final String expectedTitle = "testShouldInterceptRequestCanInterceptMainFrame";
363 final String expectedPage = makePageWithTitle(expectedTitle);
365 mShouldInterceptRequestHelper.setReturnValue(
366 stringToWebResourceResponse(expectedPage));
368 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
370 loadUrlSync(aboutPageUrl);
372 assertEquals(expectedTitle, getTitleOnUiThread());
373 assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
377 @Feature({"XWalkClientShouldInterceptRequest"})
378 public void testDoesNotChangeReportedUrl() throws Throwable {
379 mShouldInterceptRequestHelper.setReturnValue(
380 stringToWebResourceResponse(makePageWithTitle("some title")));
382 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
384 loadUrlSync(aboutPageUrl);
386 assertEquals(aboutPageUrl, mTestContentsClient.getOnPageFinishedHelper().getUrl());
387 assertEquals(aboutPageUrl, mTestContentsClient.getOnPageStartedHelper().getUrl());
391 @Feature({"XWalkClientShouldInterceptRequest"})
392 public void testNullInputStreamCausesErrorForMainFrame() throws Throwable {
393 final OnReceivedErrorHelper onReceivedErrorHelper = mTestContentsClient.getOnReceivedErrorHelper();
394 mShouldInterceptRequestHelper.setReturnValue(
395 new WebResourceResponse("text/html", "UTF-8", null));
397 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
398 final int callCount = onReceivedErrorHelper.getCallCount();
399 loadUrlAsync(aboutPageUrl);
400 onReceivedErrorHelper.waitForCallback(callCount);
401 assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
405 @Feature({"XWalkClientShouldInterceptRequest"})
406 public void testCalledForImage() throws Throwable {
407 final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
408 mWebServer.setResponseBase64(imagePath,
409 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
410 final String pageWithImage =
411 addPageToTestServer(mWebServer, "/page_with_image.html",
412 CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
414 int callCount = mShouldInterceptRequestHelper.getCallCount();
415 loadUrlSync(pageWithImage);
416 mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
418 assertEquals(2, mShouldInterceptRequestHelper.getUrls().size());
419 assertTrue(mShouldInterceptRequestHelper.getUrls().get(1).endsWith(
420 CommonResources.FAVICON_FILENAME));
424 @Feature({"XWalkClientShouldInterceptRequest"})
425 public void testOnReceivedErrorCallback() throws Throwable {
426 final OnReceivedErrorHelper onReceivedErrorHelper = mTestContentsClient.getOnReceivedErrorHelper();
427 mShouldInterceptRequestHelper.setReturnValue(new WebResourceResponse(null, null, null));
428 int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
429 loadUrlSync("foo://bar");
430 onReceivedErrorHelper.waitForCallback(onReceivedErrorHelperCallCount, 1);
434 @Feature({"XWalkClientShouldInterceptRequest"})
435 public void testNoOnReceivedErrorCallback() throws Throwable {
436 final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
437 final String imageUrl = mWebServer.setResponseBase64(imagePath,
438 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
439 final String pageWithImage =
440 addPageToTestServer(mWebServer, "/page_with_image.html",
441 CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
442 final OnReceivedErrorHelper onReceivedErrorHelper = mTestContentsClient.getOnReceivedErrorHelper();
443 mShouldInterceptRequestHelper.setReturnValueForUrl(
444 imageUrl, new WebResourceResponse(null, null, null));
445 int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
446 loadUrlSync(pageWithImage);
447 assertEquals(onReceivedErrorHelperCallCount, onReceivedErrorHelper.getCallCount());
451 @Feature({"XWalkClientShouldInterceptRequest"})
452 public void testCalledForIframe() throws Throwable {
453 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
454 final String pageWithIframe = addPageToTestServer(mWebServer, "/page_with_iframe.html",
455 CommonResources.makeHtmlPageFrom("",
456 "<iframe src=\"" + aboutPageUrl + "\"/>"));
458 int callCount = mShouldInterceptRequestHelper.getCallCount();
459 // These callbacks can race with favicon.ico callback.
460 mShouldInterceptRequestHelper.setUrlToWaitFor(aboutPageUrl);
461 loadUrlSync(pageWithIframe);
463 mShouldInterceptRequestHelper.waitForCallback(callCount, 1);
464 assertEquals(1, mShouldInterceptRequestHelper.getUrls().size());
465 assertEquals(aboutPageUrl, mShouldInterceptRequestHelper.getUrls().get(0));
468 private void calledForUrlTemplate(final String url) throws Exception {
469 int callCount = mShouldInterceptRequestHelper.getCallCount();
470 int onPageStartedCallCount = mTestContentsClient.getOnPageStartedHelper().getCallCount();
472 mShouldInterceptRequestHelper.waitForCallback(callCount);
473 assertEquals(url, mShouldInterceptRequestHelper.getUrls().get(0));
475 mTestContentsClient.getOnPageStartedHelper().waitForCallback(onPageStartedCallCount);
476 assertEquals(onPageStartedCallCount + 1,
477 mTestContentsClient.getOnPageStartedHelper().getCallCount());
480 private void notCalledForUrlTemplate(final String url) throws Exception {
481 int callCount = mShouldInterceptRequestHelper.getCallCount();
483 // The intercepting must happen before onPageFinished. Since the IPC messages from the
484 // renderer should be delivered in order waiting for onPageFinished is sufficient to
485 // 'flush' any pending interception messages.
486 assertEquals(callCount, mShouldInterceptRequestHelper.getCallCount());
490 @Feature({"XWalkClientShouldInterceptRequest"})
491 public void testCalledForUnsupportedSchemes() throws Throwable {
492 calledForUrlTemplate("foobar://resource/1");
496 @Feature({"XWalkClientShouldInterceptRequest"})
497 public void testCalledForNonexistentFiles() throws Throwable {
498 calledForUrlTemplate("file:///somewhere/something");
502 @Feature({"XWalkClientShouldInterceptRequest"})
503 public void testCalledForExistingFiles() throws Throwable {
504 final String tmpDir = getInstrumentation().getTargetContext().getCacheDir().getPath();
505 final String fileName = tmpDir + "/testfile.html";
506 final String title = "existing file title";
507 TestFileUtil.deleteFile(fileName); // Remove leftover file if any.
508 TestFileUtil.createNewHtmlFile(fileName, title, "");
509 final String existingFileUrl = "file://" + fileName;
511 int callCount = mShouldInterceptRequestHelper.getCallCount();
512 int onPageFinishedCallCount = mTestContentsClient.getOnPageFinishedHelper().getCallCount();
513 loadUrlAsync(existingFileUrl);
514 mShouldInterceptRequestHelper.waitForCallback(callCount);
515 assertEquals(existingFileUrl, mShouldInterceptRequestHelper.getUrls().get(0));
517 mTestContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
518 assertEquals(title, getTitleOnUiThread());
519 assertEquals(onPageFinishedCallCount + 1,
520 mTestContentsClient.getOnPageFinishedHelper().getCallCount());
524 @Feature({"XWalkClientShouldInterceptRequest"})
525 public void testNotCalledForExistingResource() throws Throwable {
526 notCalledForUrlTemplate("file:///android_res/raw/resource_file.html");
530 @Feature({"XWalkClientShouldInterceptRequest"})
531 public void testCalledForNonexistentResource() throws Throwable {
532 calledForUrlTemplate("file:///android_res/raw/no_file.html");
536 @Feature({"XWalkClientShouldInterceptRequest"})
537 public void testNotCalledForExistingAsset() throws Throwable {
538 notCalledForUrlTemplate("file:///android_asset/www/index.html");
542 @Feature({"XWalkClientShouldInterceptRequest"})
543 public void testCalledForNonexistentAsset() throws Throwable {
544 calledForUrlTemplate("file:///android_res/raw/no_file.html");
548 @Feature({"XWalkClientShouldInterceptRequest"})
549 public void testNotCalledForExistingContentUrl() throws Throwable {
550 final String contentResourceName = "target";
551 final String existingContentUrl = TestContentProvider.createContentUrl(contentResourceName);
552 TestContentProvider.resetResourceRequestCount(
553 getInstrumentation().getTargetContext(), contentResourceName);
555 notCalledForUrlTemplate(existingContentUrl);
557 int contentRequestCount = TestContentProvider.getResourceRequestCount(
558 getInstrumentation().getTargetContext(), contentResourceName);
559 assertEquals(1, contentRequestCount);
563 @Feature({"XWalkClientShouldInterceptRequest"})
564 public void testCalledForNonexistentContentUrl() throws Throwable {
565 calledForUrlTemplate("content://org.xwalk.core.test.NoSuchProvider/foo");