Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / xwalk / test / android / core / javatests / src / org / xwalk / core / xwview / test / XWalkClientShouldInterceptRequestTest.java
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.
5
6 package org.xwalk.core.xwview.test;
7
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;
19
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;
24
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;
33
34 /**
35  * Test case for XWalkClient.shouldInterceptRequest callback
36  *
37  * Note the major part of this file is migrated from android_webview/.
38  */
39 public class XWalkClientShouldInterceptRequestTest extends XWalkViewTestBase {
40
41     /**
42      * Customized XWalkClient implementation for shouldInterceptRequest
43      */
44     private class TestXWalkClient extends XWalkClient {
45
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;
53
54             void setReturnValue(WebResourceResponse value) {
55                 mResourceResponseReturnValue = value;
56             }
57
58             void setReturnValueForUrl(String url, WebResourceResponse value) {
59                 mReturnValuesByUrls.put(url, value);
60             }
61
62             public void setUrlToWaitFor(String url) {
63                 mUrlToWaitFor = url;
64             }
65
66             public List<String> getUrls() {
67                 assert getCallCount() > 0;
68                 return mShouldInterceptRequestUrls;
69             }
70
71             public WebResourceResponse getReturnValue(String url) {
72                 WebResourceResponse value = mReturnValuesByUrls.get(url);
73                 if (value != null) return value;
74                 return mResourceResponseReturnValue;
75             }
76
77             public void notifyCalled(String url) {
78                 if (mUrlToWaitFor == null || mUrlToWaitFor.equals(url)) {
79                     mShouldInterceptRequestUrls.add(url);
80                     notifyCalled();
81                 }
82             }
83         }
84
85         public class OnLoadResourceHelper extends CallbackHelper {
86             private String mUrl;
87
88             public String getUrl() {
89                 assert getCallCount() > 0;
90                 return mUrl;
91             }
92
93             public void notifyCalled(String url) {
94                 mUrl = url;
95                 notifyCalled();
96             }
97         }
98
99         @Override
100         public void onPageStarted(XWalkView view, String url, Bitmap favicon) {
101             mTestContentsClient.onPageStarted(url);
102         }
103
104         @Override
105         public void onPageFinished(XWalkView view, String url) {
106             mTestContentsClient.didFinishLoad(url);
107         }
108
109         @Override
110         public void onReceivedError(XWalkView view, int errorCode,
111                 String description, String failingUrl) {
112             mTestContentsClient.onReceivedError(errorCode, description, failingUrl);
113         }
114
115         @Override
116         public WebResourceResponse shouldInterceptRequest(XWalkView view, String url) {
117             WebResourceResponse response = mShouldInterceptRequestHelper.getReturnValue(url);
118             mShouldInterceptRequestHelper.notifyCalled(url);
119             return response;
120         }
121
122         @Override
123         public void onLoadResource(XWalkView view, String url) {
124             super.onLoadResource(view, url);
125             mOnLoadResourceHelper.notifyCalled(url);
126         }
127
128         private ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
129         private OnLoadResourceHelper mOnLoadResourceHelper;
130
131         public TestXWalkClient() {
132             mShouldInterceptRequestHelper = new ShouldInterceptRequestHelper();
133             mOnLoadResourceHelper = new OnLoadResourceHelper();
134         }
135
136         public ShouldInterceptRequestHelper getShouldInterceptRequestHelper() {
137             return mShouldInterceptRequestHelper;
138         }
139
140         public OnLoadResourceHelper getOnLoadResourceHelper() {
141             return mOnLoadResourceHelper;
142         }
143     }
144
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);
150     }
151
152     private String addAboutPageToTestServer(TestWebServer webServer) {
153         return addPageToTestServer(webServer, "/" + CommonResources.ABOUT_FILENAME,
154                 CommonResources.ABOUT_HTML);
155     }
156
157     private WebResourceResponse stringToWebResourceResponse(String input) throws Throwable {
158         final String mimeType = "text/html";
159         final String encoding = "UTF-8";
160
161         return new WebResourceResponse(
162                 mimeType, encoding, new ByteArrayInputStream(input.getBytes(encoding)));
163     }
164
165     private TestWebServer mWebServer;
166     private TestXWalkClient mTestXWalkClient;
167     private TestXWalkClient.ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
168     private TestXWalkClient.OnLoadResourceHelper mOnLoadResourceHelper;
169
170     @Override
171     protected void setUp() throws Exception {
172         super.setUp();
173
174         getInstrumentation().runOnMainSync(new Runnable() {
175             @Override
176             public void run() {
177                 mTestXWalkClient = new TestXWalkClient();
178                 getXWalkView().setXWalkClient(mTestXWalkClient);
179                 mShouldInterceptRequestHelper = mTestXWalkClient.getShouldInterceptRequestHelper();
180                 mOnLoadResourceHelper = mTestXWalkClient.getOnLoadResourceHelper();
181             }
182         });
183
184         mWebServer = new TestWebServer(false);
185     }
186
187     @Override
188     protected void tearDown() throws Exception {
189         mWebServer.shutdown();
190         super.tearDown();
191     }
192
193     @SmallTest
194     @Feature({"XWalkClientShouldInterceptRequest"})
195     public void testCalledWithCorrectUrl() throws Throwable {
196         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
197
198         int callCount = mShouldInterceptRequestHelper.getCallCount();
199         int onPageFinishedCallCount = mTestContentsClient.getOnPageFinishedHelper().getCallCount();
200
201         loadUrlAsync(aboutPageUrl);
202
203         mShouldInterceptRequestHelper.waitForCallback(callCount);
204         assertEquals(1, mShouldInterceptRequestHelper.getUrls().size());
205         assertEquals(aboutPageUrl,
206                 mShouldInterceptRequestHelper.getUrls().get(0));
207
208         mTestContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
209         assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread());
210     }
211
212     @SmallTest
213     @Feature({"XWalkClientShouldInterceptRequest"})
214     public void testOnLoadResourceCalledWithCorrectUrl() throws Throwable {
215         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
216         int callCount = mOnLoadResourceHelper.getCallCount();
217
218         loadUrlAsync(aboutPageUrl);
219
220         mOnLoadResourceHelper.waitForCallback(callCount);
221         assertEquals(aboutPageUrl, mOnLoadResourceHelper.getUrl());
222     }
223
224     @SmallTest
225     @Feature({"XWalkClientShouldInterceptRequest"})
226     public void testDoesNotCrashOnInvalidData() throws Throwable {
227         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
228
229         mShouldInterceptRequestHelper.setReturnValue(
230                 new WebResourceResponse("text/html", "UTF-8", null));
231         int callCount = mShouldInterceptRequestHelper.getCallCount();
232         loadUrlAsync(aboutPageUrl);
233         mShouldInterceptRequestHelper.waitForCallback(callCount);
234
235         mShouldInterceptRequestHelper.setReturnValue(
236                 new WebResourceResponse(null, null, new ByteArrayInputStream(new byte[0])));
237         callCount = mShouldInterceptRequestHelper.getCallCount();
238         loadUrlAsync(aboutPageUrl);
239         mShouldInterceptRequestHelper.waitForCallback(callCount);
240
241         mShouldInterceptRequestHelper.setReturnValue(
242                 new WebResourceResponse(null, null, null));
243         callCount = mShouldInterceptRequestHelper.getCallCount();
244         loadUrlAsync(aboutPageUrl);
245         mShouldInterceptRequestHelper.waitForCallback(callCount);
246     }
247
248     private static class EmptyInputStream extends InputStream {
249         @Override
250         public int available() {
251             return 0;
252         }
253
254         @Override
255         public int read() throws IOException {
256             return -1;
257         }
258
259         @Override
260         public int read(byte b[]) throws IOException {
261             return -1;
262         }
263
264         @Override
265         public int read(byte b[], int off, int len) throws IOException {
266             return -1;
267         }
268
269         @Override
270         public long skip(long n) throws IOException {
271             if (n < 0)
272                 throw new IOException("skipping negative number of bytes");
273             return 0;
274         }
275     }
276
277     @SmallTest
278     @Feature({"XWalkClientShouldInterceptRequest"})
279     public void testDoesNotCrashOnEmptyStream() throws Throwable {
280         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
281
282         mShouldInterceptRequestHelper.setReturnValue(
283                 new WebResourceResponse("text/html", "UTF-8", new EmptyInputStream()));
284         int shouldInterceptRequestCallCount = mShouldInterceptRequestHelper.getCallCount();
285         int onPageFinishedCallCount = mTestContentsClient.getOnPageFinishedHelper().getCallCount();
286
287         loadUrlAsync(aboutPageUrl);
288
289         mShouldInterceptRequestHelper.waitForCallback(shouldInterceptRequestCallCount);
290         mTestContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
291     }
292
293     private static class SlowWebResourceResponse extends WebResourceResponse {
294         private CallbackHelper mReadStartedCallbackHelper = new CallbackHelper();
295         private CountDownLatch mLatch = new CountDownLatch(1);
296
297         public SlowWebResourceResponse(String mimeType, String encoding, InputStream data) {
298             super(mimeType, encoding, data);
299         }
300
301         @Override
302         public InputStream getData() {
303             mReadStartedCallbackHelper.notifyCalled();
304             try {
305                 mLatch.await();
306             } catch (InterruptedException e) {
307                 // ignore
308             }
309             return super.getData();
310         }
311
312         public void unblockReads() {
313             mLatch.countDown();
314         }
315
316         public CallbackHelper getReadStartedCallbackHelper() {
317             return mReadStartedCallbackHelper;
318         }
319     }
320
321     @SmallTest
322     @Feature({"XWalkClientShouldInterceptRequest"})
323     public void testHttpStatusField() throws Throwable {
324         final String syncGetUrl = mWebServer.getResponseUrl("/intercept_me");
325         final String syncGetJs =
326             "(function() {" +
327             "  var xhr = new XMLHttpRequest();" +
328             "  xhr.open('GET', '" + syncGetUrl + "', false);" +
329             "  xhr.send(null);" +
330             "  console.info('xhr.status = ' + xhr.status);" +
331             "  return xhr.status;" +
332             "})();";
333
334         getInstrumentation().runOnMainSync(new Runnable() {
335             @Override
336             public void run() {
337                 getXWalkView().getSettings().setJavaScriptEnabled(true);
338             }
339         });
340
341         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
342         loadUrlSync(aboutPageUrl);
343
344         mShouldInterceptRequestHelper.setReturnValue(
345                 new WebResourceResponse("text/html", "UTF-8", null));
346         assertEquals("404", executeJavaScriptAndWaitForResult(syncGetJs));
347
348         mShouldInterceptRequestHelper.setReturnValue(
349                 new WebResourceResponse("text/html", "UTF-8", new EmptyInputStream()));
350         assertEquals("200", executeJavaScriptAndWaitForResult(syncGetJs));
351     }
352
353
354     private String makePageWithTitle(String title) {
355         return CommonResources.makeHtmlPageFrom("<title>" + title + "</title>",
356                 "<div> The title is: " + title + " </div>");
357     }
358
359     @SmallTest
360     @Feature({"XWalkClientShouldInterceptRequest"})
361     public void testCanInterceptMainFrame() throws Throwable {
362         final String expectedTitle = "testShouldInterceptRequestCanInterceptMainFrame";
363         final String expectedPage = makePageWithTitle(expectedTitle);
364
365         mShouldInterceptRequestHelper.setReturnValue(
366                 stringToWebResourceResponse(expectedPage));
367
368         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
369
370         loadUrlSync(aboutPageUrl);
371
372         assertEquals(expectedTitle, getTitleOnUiThread());
373         assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
374     }
375
376     @SmallTest
377     @Feature({"XWalkClientShouldInterceptRequest"})
378     public void testDoesNotChangeReportedUrl() throws Throwable {
379         mShouldInterceptRequestHelper.setReturnValue(
380                 stringToWebResourceResponse(makePageWithTitle("some title")));
381
382         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
383
384         loadUrlSync(aboutPageUrl);
385
386         assertEquals(aboutPageUrl, mTestContentsClient.getOnPageFinishedHelper().getUrl());
387         assertEquals(aboutPageUrl, mTestContentsClient.getOnPageStartedHelper().getUrl());
388     }
389
390     @SmallTest
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));
396
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));
402     }
403
404     @SmallTest
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));
413
414         int callCount = mShouldInterceptRequestHelper.getCallCount();
415         loadUrlSync(pageWithImage);
416         mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
417
418         assertEquals(2, mShouldInterceptRequestHelper.getUrls().size());
419         assertTrue(mShouldInterceptRequestHelper.getUrls().get(1).endsWith(
420                 CommonResources.FAVICON_FILENAME));
421     }
422
423     @SmallTest
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);
431     }
432
433     @SmallTest
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());
448     }
449
450     @SmallTest
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 + "\"/>"));
457
458         int callCount = mShouldInterceptRequestHelper.getCallCount();
459         // These callbacks can race with favicon.ico callback.
460         mShouldInterceptRequestHelper.setUrlToWaitFor(aboutPageUrl);
461         loadUrlSync(pageWithIframe);
462
463         mShouldInterceptRequestHelper.waitForCallback(callCount, 1);
464         assertEquals(1, mShouldInterceptRequestHelper.getUrls().size());
465         assertEquals(aboutPageUrl, mShouldInterceptRequestHelper.getUrls().get(0));
466     }
467
468     private void calledForUrlTemplate(final String url) throws Exception {
469         int callCount = mShouldInterceptRequestHelper.getCallCount();
470         int onPageStartedCallCount = mTestContentsClient.getOnPageStartedHelper().getCallCount();
471         loadUrlAsync(url);
472         mShouldInterceptRequestHelper.waitForCallback(callCount);
473         assertEquals(url, mShouldInterceptRequestHelper.getUrls().get(0));
474
475         mTestContentsClient.getOnPageStartedHelper().waitForCallback(onPageStartedCallCount);
476         assertEquals(onPageStartedCallCount + 1,
477                 mTestContentsClient.getOnPageStartedHelper().getCallCount());
478     }
479
480     private void notCalledForUrlTemplate(final String url) throws Exception {
481         int callCount = mShouldInterceptRequestHelper.getCallCount();
482         loadUrlSync(url);
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());
487     }
488
489     @SmallTest
490     @Feature({"XWalkClientShouldInterceptRequest"})
491     public void testCalledForUnsupportedSchemes() throws Throwable {
492         calledForUrlTemplate("foobar://resource/1");
493     }
494
495     @SmallTest
496     @Feature({"XWalkClientShouldInterceptRequest"})
497     public void testCalledForNonexistentFiles() throws Throwable {
498         calledForUrlTemplate("file:///somewhere/something");
499     }
500
501     @SmallTest
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;
510
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));
516
517         mTestContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
518         assertEquals(title, getTitleOnUiThread());
519         assertEquals(onPageFinishedCallCount + 1,
520                 mTestContentsClient.getOnPageFinishedHelper().getCallCount());
521     }
522
523     @SmallTest
524     @Feature({"XWalkClientShouldInterceptRequest"})
525     public void testNotCalledForExistingResource() throws Throwable {
526         notCalledForUrlTemplate("file:///android_res/raw/resource_file.html");
527     }
528
529     @SmallTest
530     @Feature({"XWalkClientShouldInterceptRequest"})
531     public void testCalledForNonexistentResource() throws Throwable {
532         calledForUrlTemplate("file:///android_res/raw/no_file.html");
533     }
534
535     @SmallTest
536     @Feature({"XWalkClientShouldInterceptRequest"})
537     public void testNotCalledForExistingAsset() throws Throwable {
538         notCalledForUrlTemplate("file:///android_asset/www/index.html");
539     }
540
541     @SmallTest
542     @Feature({"XWalkClientShouldInterceptRequest"})
543     public void testCalledForNonexistentAsset() throws Throwable {
544         calledForUrlTemplate("file:///android_res/raw/no_file.html");
545     }
546
547     @SmallTest
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);
554
555         notCalledForUrlTemplate(existingContentUrl);
556
557         int contentRequestCount = TestContentProvider.getResourceRequestCount(
558                 getInstrumentation().getTargetContext(), contentResourceName);
559         assertEquals(1, contentRequestCount);
560     }
561
562     @SmallTest
563     @Feature({"XWalkClientShouldInterceptRequest"})
564     public void testCalledForNonexistentContentUrl() throws Throwable {
565         calledForUrlTemplate("content://org.xwalk.core.test.NoSuchProvider/foo");
566     }
567 }