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 // TODO(gaochun): Enable it once the issue XWALK-1022 gets resolved.
325 public void testHttpStatusField() throws Throwable {
326 final String syncGetUrl = mWebServer.getResponseUrl("/intercept_me");
327 final String syncGetJs =
329 " var xhr = new XMLHttpRequest();" +
330 " xhr.open('GET', '" + syncGetUrl + "', false);" +
332 " console.info('xhr.status = ' + xhr.status);" +
333 " return xhr.status;" +
336 getInstrumentation().runOnMainSync(new Runnable() {
339 getXWalkView().getSettings().setJavaScriptEnabled(true);
343 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
344 loadUrlSync(aboutPageUrl);
346 mShouldInterceptRequestHelper.setReturnValue(
347 new WebResourceResponse("text/html", "UTF-8", null));
348 assertEquals("404", executeJavaScriptAndWaitForResult(syncGetJs));
350 mShouldInterceptRequestHelper.setReturnValue(
351 new WebResourceResponse("text/html", "UTF-8", new EmptyInputStream()));
352 assertEquals("200", executeJavaScriptAndWaitForResult(syncGetJs));
356 private String makePageWithTitle(String title) {
357 return CommonResources.makeHtmlPageFrom("<title>" + title + "</title>",
358 "<div> The title is: " + title + " </div>");
362 @Feature({"XWalkClientShouldInterceptRequest"})
363 public void testCanInterceptMainFrame() throws Throwable {
364 final String expectedTitle = "testShouldInterceptRequestCanInterceptMainFrame";
365 final String expectedPage = makePageWithTitle(expectedTitle);
367 mShouldInterceptRequestHelper.setReturnValue(
368 stringToWebResourceResponse(expectedPage));
370 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
372 loadUrlSync(aboutPageUrl);
374 assertEquals(expectedTitle, getTitleOnUiThread());
375 assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
379 @Feature({"XWalkClientShouldInterceptRequest"})
380 public void testDoesNotChangeReportedUrl() throws Throwable {
381 mShouldInterceptRequestHelper.setReturnValue(
382 stringToWebResourceResponse(makePageWithTitle("some title")));
384 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
386 loadUrlSync(aboutPageUrl);
388 assertEquals(aboutPageUrl, mTestContentsClient.getOnPageFinishedHelper().getUrl());
389 assertEquals(aboutPageUrl, mTestContentsClient.getOnPageStartedHelper().getUrl());
393 @Feature({"XWalkClientShouldInterceptRequest"})
394 public void testNullInputStreamCausesErrorForMainFrame() throws Throwable {
395 final OnReceivedErrorHelper onReceivedErrorHelper = mTestContentsClient.getOnReceivedErrorHelper();
396 mShouldInterceptRequestHelper.setReturnValue(
397 new WebResourceResponse("text/html", "UTF-8", null));
399 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
400 final int callCount = onReceivedErrorHelper.getCallCount();
401 loadUrlAsync(aboutPageUrl);
402 onReceivedErrorHelper.waitForCallback(callCount);
403 assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
407 @Feature({"XWalkClientShouldInterceptRequest"})
408 public void testCalledForImage() throws Throwable {
409 final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
410 mWebServer.setResponseBase64(imagePath,
411 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
412 final String pageWithImage =
413 addPageToTestServer(mWebServer, "/page_with_image.html",
414 CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
416 int callCount = mShouldInterceptRequestHelper.getCallCount();
417 loadUrlSync(pageWithImage);
418 mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
420 assertEquals(2, mShouldInterceptRequestHelper.getUrls().size());
421 assertTrue(mShouldInterceptRequestHelper.getUrls().get(1).endsWith(
422 CommonResources.FAVICON_FILENAME));
426 @Feature({"XWalkClientShouldInterceptRequest"})
427 public void testOnReceivedErrorCallback() throws Throwable {
428 final OnReceivedErrorHelper onReceivedErrorHelper = mTestContentsClient.getOnReceivedErrorHelper();
429 mShouldInterceptRequestHelper.setReturnValue(new WebResourceResponse(null, null, null));
430 int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
431 loadUrlSync("foo://bar");
432 onReceivedErrorHelper.waitForCallback(onReceivedErrorHelperCallCount, 1);
436 @Feature({"XWalkClientShouldInterceptRequest"})
437 public void testNoOnReceivedErrorCallback() throws Throwable {
438 final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
439 final String imageUrl = mWebServer.setResponseBase64(imagePath,
440 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
441 final String pageWithImage =
442 addPageToTestServer(mWebServer, "/page_with_image.html",
443 CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
444 final OnReceivedErrorHelper onReceivedErrorHelper = mTestContentsClient.getOnReceivedErrorHelper();
445 mShouldInterceptRequestHelper.setReturnValueForUrl(
446 imageUrl, new WebResourceResponse(null, null, null));
447 int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
448 loadUrlSync(pageWithImage);
449 assertEquals(onReceivedErrorHelperCallCount, onReceivedErrorHelper.getCallCount());
453 @Feature({"XWalkClientShouldInterceptRequest"})
454 public void testCalledForIframe() throws Throwable {
455 final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
456 final String pageWithIframe = addPageToTestServer(mWebServer, "/page_with_iframe.html",
457 CommonResources.makeHtmlPageFrom("",
458 "<iframe src=\"" + aboutPageUrl + "\"/>"));
460 int callCount = mShouldInterceptRequestHelper.getCallCount();
461 // These callbacks can race with favicon.ico callback.
462 mShouldInterceptRequestHelper.setUrlToWaitFor(aboutPageUrl);
463 loadUrlSync(pageWithIframe);
465 mShouldInterceptRequestHelper.waitForCallback(callCount, 1);
466 assertEquals(1, mShouldInterceptRequestHelper.getUrls().size());
467 assertEquals(aboutPageUrl, mShouldInterceptRequestHelper.getUrls().get(0));
470 private void calledForUrlTemplate(final String url) throws Exception {
471 int callCount = mShouldInterceptRequestHelper.getCallCount();
472 int onPageStartedCallCount = mTestContentsClient.getOnPageStartedHelper().getCallCount();
474 mShouldInterceptRequestHelper.waitForCallback(callCount);
475 assertEquals(url, mShouldInterceptRequestHelper.getUrls().get(0));
477 mTestContentsClient.getOnPageStartedHelper().waitForCallback(onPageStartedCallCount);
478 assertEquals(onPageStartedCallCount + 1,
479 mTestContentsClient.getOnPageStartedHelper().getCallCount());
482 private void notCalledForUrlTemplate(final String url) throws Exception {
483 int callCount = mShouldInterceptRequestHelper.getCallCount();
485 // The intercepting must happen before onPageFinished. Since the IPC messages from the
486 // renderer should be delivered in order waiting for onPageFinished is sufficient to
487 // 'flush' any pending interception messages.
488 assertEquals(callCount, mShouldInterceptRequestHelper.getCallCount());
492 @Feature({"XWalkClientShouldInterceptRequest"})
493 public void testCalledForUnsupportedSchemes() throws Throwable {
494 calledForUrlTemplate("foobar://resource/1");
498 @Feature({"XWalkClientShouldInterceptRequest"})
499 public void testCalledForNonexistentFiles() throws Throwable {
500 calledForUrlTemplate("file:///somewhere/something");
504 @Feature({"XWalkClientShouldInterceptRequest"})
505 public void testCalledForExistingFiles() throws Throwable {
506 final String tmpDir = getInstrumentation().getTargetContext().getCacheDir().getPath();
507 final String fileName = tmpDir + "/testfile.html";
508 final String title = "existing file title";
509 TestFileUtil.deleteFile(fileName); // Remove leftover file if any.
510 TestFileUtil.createNewHtmlFile(fileName, title, "");
511 final String existingFileUrl = "file://" + fileName;
513 int callCount = mShouldInterceptRequestHelper.getCallCount();
514 int onPageFinishedCallCount = mTestContentsClient.getOnPageFinishedHelper().getCallCount();
515 loadUrlAsync(existingFileUrl);
516 mShouldInterceptRequestHelper.waitForCallback(callCount);
517 assertEquals(existingFileUrl, mShouldInterceptRequestHelper.getUrls().get(0));
519 mTestContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
520 assertEquals(title, getTitleOnUiThread());
521 assertEquals(onPageFinishedCallCount + 1,
522 mTestContentsClient.getOnPageFinishedHelper().getCallCount());
526 @Feature({"XWalkClientShouldInterceptRequest"})
527 public void testNotCalledForExistingResource() throws Throwable {
528 notCalledForUrlTemplate("file:///android_res/raw/resource_file.html");
532 @Feature({"XWalkClientShouldInterceptRequest"})
533 public void testCalledForNonexistentResource() throws Throwable {
534 calledForUrlTemplate("file:///android_res/raw/no_file.html");
538 @Feature({"XWalkClientShouldInterceptRequest"})
539 public void testNotCalledForExistingAsset() throws Throwable {
540 notCalledForUrlTemplate("file:///android_asset/www/index.html");
544 @Feature({"XWalkClientShouldInterceptRequest"})
545 public void testCalledForNonexistentAsset() throws Throwable {
546 calledForUrlTemplate("file:///android_res/raw/no_file.html");
550 @Feature({"XWalkClientShouldInterceptRequest"})
551 public void testNotCalledForExistingContentUrl() throws Throwable {
552 final String contentResourceName = "target";
553 final String existingContentUrl = TestContentProvider.createContentUrl(contentResourceName);
554 TestContentProvider.resetResourceRequestCount(
555 getInstrumentation().getTargetContext(), contentResourceName);
557 notCalledForUrlTemplate(existingContentUrl);
559 int contentRequestCount = TestContentProvider.getResourceRequestCount(
560 getInstrumentation().getTargetContext(), contentResourceName);
561 assertEquals(1, contentRequestCount);
565 @Feature({"XWalkClientShouldInterceptRequest"})
566 public void testCalledForNonexistentContentUrl() throws Throwable {
567 calledForUrlTemplate("content://org.xwalk.core.test.NoSuchProvider/foo");