- add sources.
[platform/framework/web/crosswalk.git] / src / android_webview / javatests / src / org / chromium / android_webview / test / AwContentsClientShouldInterceptRequestTest.java
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
5 package org.chromium.android_webview.test;
6
7 import android.test.suitebuilder.annotation.SmallTest;
8 import android.util.Log;
9 import android.util.Pair;
10
11 import org.chromium.android_webview.AndroidProtocolHandler;
12 import org.chromium.android_webview.AwContents;
13 import org.chromium.android_webview.InterceptedRequestData;
14 import org.chromium.android_webview.test.util.CommonResources;
15 import org.chromium.android_webview.test.util.JSUtils;
16 import org.chromium.base.test.util.Feature;
17 import org.chromium.base.test.util.TestFileUtil;
18 import org.chromium.content.browser.test.util.CallbackHelper;
19 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnPageFinishedHelper;
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;
23
24 import java.io.ByteArrayInputStream;
25 import java.io.InputStream;
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.List;
30 import java.util.Random;
31
32 /**
33  * Tests for the WebViewClient.shouldInterceptRequest() method.
34  */
35 public class AwContentsClientShouldInterceptRequestTest extends AwTestBase {
36
37     private static class TestAwContentsClient
38             extends org.chromium.android_webview.test.TestAwContentsClient {
39
40         public static class ShouldInterceptRequestHelper extends CallbackHelper {
41             private List<String> mShouldInterceptRequestUrls = new ArrayList<String>();
42             private ConcurrentHashMap<String, InterceptedRequestData> mReturnValuesByUrls
43                 = new ConcurrentHashMap<String, InterceptedRequestData>();
44             // This is read from the IO thread, so needs to be marked volatile.
45             private volatile InterceptedRequestData mShouldInterceptRequestReturnValue = null;
46             void setReturnValue(InterceptedRequestData value) {
47                 mShouldInterceptRequestReturnValue = value;
48             }
49             void setReturnValueForUrl(String url, InterceptedRequestData value) {
50                 mReturnValuesByUrls.put(url, value);
51             }
52             public List<String> getUrls() {
53                 assert getCallCount() > 0;
54                 return mShouldInterceptRequestUrls;
55             }
56             public InterceptedRequestData getReturnValue(String url) {
57                 InterceptedRequestData value = mReturnValuesByUrls.get(url);
58                 if (value != null) return value;
59                 return mShouldInterceptRequestReturnValue;
60             }
61             public void notifyCalled(String url) {
62                 mShouldInterceptRequestUrls.add(url);
63                 notifyCalled();
64             }
65         }
66
67         public static class OnLoadResourceHelper extends CallbackHelper {
68             private String mUrl;
69
70             public String getUrl() {
71                 assert getCallCount() > 0;
72                 return mUrl;
73             }
74
75             public void notifyCalled(String url) {
76                 mUrl = url;
77                 notifyCalled();
78             }
79         }
80
81         @Override
82         public InterceptedRequestData shouldInterceptRequest(String url) {
83             InterceptedRequestData returnValue = mShouldInterceptRequestHelper.getReturnValue(url);
84             mShouldInterceptRequestHelper.notifyCalled(url);
85             return returnValue;
86         }
87
88         @Override
89         public void onLoadResource(String url) {
90             super.onLoadResource(url);
91             mOnLoadResourceHelper.notifyCalled(url);
92         }
93
94         private ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
95         private OnLoadResourceHelper mOnLoadResourceHelper;
96
97         public TestAwContentsClient() {
98             mShouldInterceptRequestHelper = new ShouldInterceptRequestHelper();
99             mOnLoadResourceHelper = new OnLoadResourceHelper();
100         }
101
102         public ShouldInterceptRequestHelper getShouldInterceptRequestHelper() {
103             return mShouldInterceptRequestHelper;
104         }
105
106         public OnLoadResourceHelper getOnLoadResourceHelper() {
107             return mOnLoadResourceHelper;
108         }
109     }
110
111     private String addPageToTestServer(TestWebServer webServer, String httpPath, String html) {
112         List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
113         headers.add(Pair.create("Content-Type", "text/html"));
114         headers.add(Pair.create("Cache-Control", "no-store"));
115         return webServer.setResponse(httpPath, html, headers);
116     }
117
118     private String addAboutPageToTestServer(TestWebServer webServer) {
119         return addPageToTestServer(webServer, "/" + CommonResources.ABOUT_FILENAME,
120                 CommonResources.ABOUT_HTML);
121     }
122
123     private InterceptedRequestData stringToInterceptedRequestData(String input) throws Throwable {
124         final String mimeType = "text/html";
125         final String encoding = "UTF-8";
126
127         return new InterceptedRequestData(
128                 mimeType, encoding, new ByteArrayInputStream(input.getBytes(encoding)));
129     }
130
131     private TestWebServer mWebServer;
132     private TestAwContentsClient mContentsClient;
133     private AwTestContainerView mTestContainerView;
134     private AwContents mAwContents;
135     private TestAwContentsClient.ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
136
137     @Override
138     protected void setUp() throws Exception {
139         super.setUp();
140
141         mContentsClient = new TestAwContentsClient();
142         mTestContainerView = createAwTestContainerViewOnMainSync(mContentsClient);
143         mAwContents = mTestContainerView.getAwContents();
144         mShouldInterceptRequestHelper = mContentsClient.getShouldInterceptRequestHelper();
145
146         mWebServer = new TestWebServer(false);
147     }
148
149     @Override
150     protected void tearDown() throws Exception {
151         mWebServer.shutdown();
152         super.tearDown();
153     }
154
155     @SmallTest
156     @Feature({"AndroidWebView"})
157     public void testCalledWithCorrectUrl() throws Throwable {
158         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
159
160         int callCount = mShouldInterceptRequestHelper.getCallCount();
161         int onPageFinishedCallCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
162
163         loadUrlAsync(mAwContents, aboutPageUrl);
164
165         mShouldInterceptRequestHelper.waitForCallback(callCount);
166         assertEquals(1, mShouldInterceptRequestHelper.getUrls().size());
167         assertEquals(aboutPageUrl,
168                 mShouldInterceptRequestHelper.getUrls().get(0));
169
170         mContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
171         assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread(mAwContents));
172     }
173
174     @SmallTest
175     @Feature({"AndroidWebView"})
176     public void testOnLoadResourceCalledWithCorrectUrl() throws Throwable {
177         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
178         final TestAwContentsClient.OnLoadResourceHelper onLoadResourceHelper =
179             mContentsClient.getOnLoadResourceHelper();
180
181         int callCount = onLoadResourceHelper.getCallCount();
182
183         loadUrlAsync(mAwContents, aboutPageUrl);
184
185         onLoadResourceHelper.waitForCallback(callCount);
186         assertEquals(aboutPageUrl, onLoadResourceHelper.getUrl());
187     }
188
189     @SmallTest
190     @Feature({"AndroidWebView"})
191     public void testDoesNotCrashOnInvalidData() throws Throwable {
192         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
193
194         mShouldInterceptRequestHelper.setReturnValue(
195                 new InterceptedRequestData("text/html", "UTF-8", null));
196         int callCount = mShouldInterceptRequestHelper.getCallCount();
197         loadUrlAsync(mAwContents, aboutPageUrl);
198         mShouldInterceptRequestHelper.waitForCallback(callCount);
199
200         mShouldInterceptRequestHelper.setReturnValue(
201                 new InterceptedRequestData(null, null, new ByteArrayInputStream(new byte[0])));
202         callCount = mShouldInterceptRequestHelper.getCallCount();
203         loadUrlAsync(mAwContents, aboutPageUrl);
204         mShouldInterceptRequestHelper.waitForCallback(callCount);
205
206         mShouldInterceptRequestHelper.setReturnValue(
207                 new InterceptedRequestData(null, null, null));
208         callCount = mShouldInterceptRequestHelper.getCallCount();
209         loadUrlAsync(mAwContents, aboutPageUrl);
210         mShouldInterceptRequestHelper.waitForCallback(callCount);
211     }
212
213     private static class EmptyInputStream extends InputStream {
214         @Override
215         public int available() {
216             return 0;
217         }
218
219         @Override
220         public int read() throws IOException {
221             return -1;
222         }
223
224         @Override
225         public int read(byte b[]) throws IOException {
226             return -1;
227         }
228
229         @Override
230         public int read(byte b[], int off, int len) throws IOException {
231             return -1;
232         }
233
234         @Override
235         public long skip(long n) throws IOException {
236             if (n < 0)
237                 throw new IOException("skipping negative number of bytes");
238             return 0;
239         }
240     }
241
242     @SmallTest
243     @Feature({"AndroidWebView"})
244     public void testDoesNotCrashOnEmptyStream() throws Throwable {
245         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
246
247         mShouldInterceptRequestHelper.setReturnValue(
248                 new InterceptedRequestData("text/html", "UTF-8", new EmptyInputStream()));
249         int shouldInterceptRequestCallCount = mShouldInterceptRequestHelper.getCallCount();
250         int onPageFinishedCallCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
251
252         loadUrlAsync(mAwContents, aboutPageUrl);
253
254         mShouldInterceptRequestHelper.waitForCallback(shouldInterceptRequestCallCount);
255         mContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
256     }
257
258     @SmallTest
259     @Feature({"AndroidWebView"})
260     public void testHttpStatusField() throws Throwable {
261         final String syncGetUrl = mWebServer.getResponseUrl("/intercept_me");
262         final String syncGetJs =
263             "(function() {" +
264             "  var xhr = new XMLHttpRequest();" +
265             "  xhr.open('GET', '" + syncGetUrl + "', false);" +
266             "  xhr.send(null);" +
267             "  console.info('xhr.status = ' + xhr.status);" +
268             "  return xhr.status;" +
269             "})();";
270         enableJavaScriptOnUiThread(mAwContents);
271
272         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
273         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
274
275         mShouldInterceptRequestHelper.setReturnValue(
276                 new InterceptedRequestData("text/html", "UTF-8", null));
277         assertEquals("404",
278                 executeJavaScriptAndWaitForResult(mAwContents, mContentsClient, syncGetJs));
279
280         mShouldInterceptRequestHelper.setReturnValue(
281                 new InterceptedRequestData("text/html", "UTF-8", new EmptyInputStream()));
282         assertEquals("200",
283                 executeJavaScriptAndWaitForResult(mAwContents, mContentsClient, syncGetJs));
284     }
285
286
287     private String makePageWithTitle(String title) {
288         return CommonResources.makeHtmlPageFrom("<title>" + title + "</title>",
289                 "<div> The title is: " + title + " </div>");
290     }
291
292     @SmallTest
293     @Feature({"AndroidWebView"})
294     public void testCanInterceptMainFrame() throws Throwable {
295         final String expectedTitle = "testShouldInterceptRequestCanInterceptMainFrame";
296         final String expectedPage = makePageWithTitle(expectedTitle);
297
298         mShouldInterceptRequestHelper.setReturnValue(
299                 stringToInterceptedRequestData(expectedPage));
300
301         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
302
303         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
304
305         assertEquals(expectedTitle, getTitleOnUiThread(mAwContents));
306         assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
307     }
308
309     @SmallTest
310     @Feature({"AndroidWebView"})
311     public void testDoesNotChangeReportedUrl() throws Throwable {
312         mShouldInterceptRequestHelper.setReturnValue(
313                 stringToInterceptedRequestData(makePageWithTitle("some title")));
314
315         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
316
317         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
318
319         assertEquals(aboutPageUrl, mContentsClient.getOnPageFinishedHelper().getUrl());
320         assertEquals(aboutPageUrl, mContentsClient.getOnPageStartedHelper().getUrl());
321     }
322
323     @SmallTest
324     @Feature({"AndroidWebView"})
325     public void testNullInputStreamCausesErrorForMainFrame() throws Throwable {
326         final OnReceivedErrorHelper onReceivedErrorHelper =
327             mContentsClient.getOnReceivedErrorHelper();
328
329         mShouldInterceptRequestHelper.setReturnValue(
330                 new InterceptedRequestData("text/html", "UTF-8", null));
331
332         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
333         final int callCount = onReceivedErrorHelper.getCallCount();
334         loadUrlAsync(mAwContents, aboutPageUrl);
335         onReceivedErrorHelper.waitForCallback(callCount);
336         assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
337     }
338
339     @SmallTest
340     @Feature({"AndroidWebView"})
341     public void testCalledForImage() throws Throwable {
342         final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
343         mWebServer.setResponseBase64(imagePath,
344                 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
345         final String pageWithImage =
346             addPageToTestServer(mWebServer, "/page_with_image.html",
347                     CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
348
349         int callCount = mShouldInterceptRequestHelper.getCallCount();
350         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithImage);
351         mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
352
353         assertEquals(2, mShouldInterceptRequestHelper.getUrls().size());
354         assertTrue(mShouldInterceptRequestHelper.getUrls().get(1).endsWith(
355                 CommonResources.FAVICON_FILENAME));
356     }
357
358     @SmallTest
359     @Feature({"AndroidWebView"})
360     public void testOnReceivedErrorCallback() throws Throwable {
361         mShouldInterceptRequestHelper.setReturnValue(new InterceptedRequestData(null, null, null));
362         OnReceivedErrorHelper onReceivedErrorHelper = mContentsClient.getOnReceivedErrorHelper();
363         int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
364         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), "foo://bar");
365         onReceivedErrorHelper.waitForCallback(onReceivedErrorHelperCallCount, 1);
366     }
367
368     @SmallTest
369     @Feature({"AndroidWebView"})
370     public void testNoOnReceivedErrorCallback() throws Throwable {
371         final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
372         final String imageUrl = mWebServer.setResponseBase64(imagePath,
373                 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
374         final String pageWithImage =
375                 addPageToTestServer(mWebServer, "/page_with_image.html",
376                         CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
377         mShouldInterceptRequestHelper.setReturnValueForUrl(
378                 imageUrl, new InterceptedRequestData(null, null, null));
379         OnReceivedErrorHelper onReceivedErrorHelper = mContentsClient.getOnReceivedErrorHelper();
380         int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
381         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithImage);
382         assertEquals(onReceivedErrorHelperCallCount, onReceivedErrorHelper.getCallCount());
383     }
384
385     @SmallTest
386     @Feature({"AndroidWebView"})
387     public void testCalledForIframe() throws Throwable {
388         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
389         final String pageWithIframe = addPageToTestServer(mWebServer, "/page_with_iframe.html",
390                 CommonResources.makeHtmlPageFrom("",
391                     "<iframe src=\"" + aboutPageUrl + "\"/>"));
392
393         int callCount = mShouldInterceptRequestHelper.getCallCount();
394         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithIframe);
395         mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
396         assertEquals(2, mShouldInterceptRequestHelper.getUrls().size());
397         assertEquals(aboutPageUrl, mShouldInterceptRequestHelper.getUrls().get(1));
398     }
399
400     private void calledForUrlTemplate(final String url) throws Exception {
401         int callCount = mShouldInterceptRequestHelper.getCallCount();
402         int onPageStartedCallCount = mContentsClient.getOnPageStartedHelper().getCallCount();
403         loadUrlAsync(mAwContents, url);
404         mShouldInterceptRequestHelper.waitForCallback(callCount);
405         assertEquals(url, mShouldInterceptRequestHelper.getUrls().get(0));
406
407         mContentsClient.getOnPageStartedHelper().waitForCallback(onPageStartedCallCount);
408         assertEquals(onPageStartedCallCount + 1,
409                 mContentsClient.getOnPageStartedHelper().getCallCount());
410     }
411
412     private void notCalledForUrlTemplate(final String url) throws Exception {
413         int callCount = mShouldInterceptRequestHelper.getCallCount();
414         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
415         // The intercepting must happen before onPageFinished. Since the IPC messages from the
416         // renderer should be delivered in order waiting for onPageFinished is sufficient to
417         // 'flush' any pending interception messages.
418         assertEquals(callCount, mShouldInterceptRequestHelper.getCallCount());
419     }
420
421     @SmallTest
422     @Feature({"AndroidWebView"})
423     public void testCalledForUnsupportedSchemes() throws Throwable {
424         calledForUrlTemplate("foobar://resource/1");
425     }
426
427     @SmallTest
428     @Feature({"AndroidWebView"})
429     public void testCalledForNonexistentFiles() throws Throwable {
430         calledForUrlTemplate("file:///somewhere/something");
431     }
432
433     @SmallTest
434     @Feature({"AndroidWebView"})
435     public void testCalledForExistingFiles() throws Throwable {
436         final String tmpDir = getInstrumentation().getTargetContext().getCacheDir().getPath();
437         final String fileName = tmpDir + "/testfile.html";
438         final String title = "existing file title";
439         TestFileUtil.deleteFile(fileName);  // Remove leftover file if any.
440         TestFileUtil.createNewHtmlFile(fileName, title, "");
441         final String existingFileUrl = "file://" + fileName;
442
443         int callCount = mShouldInterceptRequestHelper.getCallCount();
444         int onPageFinishedCallCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
445         loadUrlAsync(mAwContents, existingFileUrl);
446         mShouldInterceptRequestHelper.waitForCallback(callCount);
447         assertEquals(existingFileUrl, mShouldInterceptRequestHelper.getUrls().get(0));
448
449         mContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
450         assertEquals(title, getTitleOnUiThread(mAwContents));
451         assertEquals(onPageFinishedCallCount + 1,
452                 mContentsClient.getOnPageFinishedHelper().getCallCount());
453     }
454
455     @SmallTest
456     @Feature({"AndroidWebView"})
457     public void testNotCalledForExistingResource() throws Throwable {
458         notCalledForUrlTemplate("file:///android_res/raw/resource_file.html");
459     }
460
461     @SmallTest
462     @Feature({"AndroidWebView"})
463     public void testCalledForNonexistentResource() throws Throwable {
464         calledForUrlTemplate("file:///android_res/raw/no_file.html");
465     }
466
467     @SmallTest
468     @Feature({"AndroidWebView"})
469     public void testNotCalledForExistingAsset() throws Throwable {
470         notCalledForUrlTemplate("file:///android_asset/asset_file.html");
471     }
472
473     @SmallTest
474     @Feature({"AndroidWebView"})
475     public void testCalledForNonexistentAsset() throws Throwable {
476         calledForUrlTemplate("file:///android_res/raw/no_file.html");
477     }
478
479     @SmallTest
480     @Feature({"AndroidWebView"})
481     public void testNotCalledForExistingContentUrl() throws Throwable {
482         final String contentResourceName = "target";
483         final String existingContentUrl = TestContentProvider.createContentUrl(contentResourceName);
484
485         notCalledForUrlTemplate(existingContentUrl);
486
487         int contentRequestCount = TestContentProvider.getResourceRequestCount(
488                 getInstrumentation().getTargetContext(), contentResourceName);
489         assertEquals(1, contentRequestCount);
490     }
491
492     @SmallTest
493     @Feature({"AndroidWebView"})
494     public void testCalledForNonexistentContentUrl() throws Throwable {
495         calledForUrlTemplate("content://org.chromium.webview.NoSuchProvider/foo");
496     }
497 }