8cc47d532d923ef6d47bded370eceeeec112e920
[platform/framework/web/crosswalk.git] / src / android_webview / javatests / src / org / chromium / android_webview / test / AwContentsClientShouldInterceptRequestTest.java
1 // Copyright 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.graphics.Bitmap;
8 import android.graphics.Canvas;
9 import android.graphics.Color;
10 import android.test.suitebuilder.annotation.SmallTest;
11 import android.util.Pair;
12
13 import org.chromium.android_webview.AwContents;
14 import org.chromium.android_webview.AwContentsClient.ShouldInterceptRequestParams;
15 import org.chromium.android_webview.AwWebResourceResponse;
16 import org.chromium.android_webview.test.util.AwTestTouchUtils;
17 import org.chromium.android_webview.test.util.CommonResources;
18 import org.chromium.android_webview.test.util.JSUtils;
19 import org.chromium.base.test.util.Feature;
20 import org.chromium.base.test.util.TestFileUtil;
21 import org.chromium.content.browser.test.util.CallbackHelper;
22 import org.chromium.content.browser.test.util.TestCallbackHelperContainer.OnReceivedErrorHelper;
23 import org.chromium.net.test.util.TestWebServer;
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.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.concurrent.Callable;
33 import java.util.concurrent.ConcurrentHashMap;
34 import java.util.concurrent.CountDownLatch;
35
36 /**
37  * Tests for the WebViewClient.shouldInterceptRequest() method.
38  */
39 public class AwContentsClientShouldInterceptRequestTest extends AwTestBase {
40
41     private static class TestAwContentsClient
42             extends org.chromium.android_webview.test.TestAwContentsClient {
43
44         public static class ShouldInterceptRequestHelper extends CallbackHelper {
45             private List<String> mShouldInterceptRequestUrls = new ArrayList<String>();
46             private ConcurrentHashMap<String, AwWebResourceResponse> mReturnValuesByUrls
47                 = new ConcurrentHashMap<String, AwWebResourceResponse>();
48             private ConcurrentHashMap<String, ShouldInterceptRequestParams> mParamsByUrls
49                 = new ConcurrentHashMap<String, ShouldInterceptRequestParams>();
50             // This is read from the IO thread, so needs to be marked volatile.
51             private volatile AwWebResourceResponse mShouldInterceptRequestReturnValue = null;
52             void setReturnValue(AwWebResourceResponse value) {
53                 mShouldInterceptRequestReturnValue = value;
54             }
55             void setReturnValueForUrl(String url, AwWebResourceResponse value) {
56                 mReturnValuesByUrls.put(url, value);
57             }
58             public List<String> getUrls() {
59                 assert getCallCount() > 0;
60                 return mShouldInterceptRequestUrls;
61             }
62             public AwWebResourceResponse getReturnValue(String url) {
63                 AwWebResourceResponse value = mReturnValuesByUrls.get(url);
64                 if (value != null) return value;
65                 return mShouldInterceptRequestReturnValue;
66             }
67             public ShouldInterceptRequestParams getParamsForUrl(String url) {
68                 assert getCallCount() > 0;
69                 assert mParamsByUrls.containsKey(url);
70                 return mParamsByUrls.get(url);
71             }
72             public void notifyCalled(ShouldInterceptRequestParams params) {
73                 mShouldInterceptRequestUrls.add(params.url);
74                 mParamsByUrls.put(params.url, params);
75                 notifyCalled();
76             }
77         }
78
79         public static class OnLoadResourceHelper extends CallbackHelper {
80             private String mUrl;
81
82             public String getUrl() {
83                 assert getCallCount() > 0;
84                 return mUrl;
85             }
86
87             public void notifyCalled(String url) {
88                 mUrl = url;
89                 notifyCalled();
90             }
91         }
92
93         @Override
94         public AwWebResourceResponse shouldInterceptRequest(ShouldInterceptRequestParams params) {
95             AwWebResourceResponse returnValue =
96                 mShouldInterceptRequestHelper.getReturnValue(params.url);
97             mShouldInterceptRequestHelper.notifyCalled(params);
98             return returnValue;
99         }
100
101         @Override
102         public void onLoadResource(String url) {
103             super.onLoadResource(url);
104             mOnLoadResourceHelper.notifyCalled(url);
105         }
106
107         private ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
108         private OnLoadResourceHelper mOnLoadResourceHelper;
109
110         public TestAwContentsClient() {
111             mShouldInterceptRequestHelper = new ShouldInterceptRequestHelper();
112             mOnLoadResourceHelper = new OnLoadResourceHelper();
113         }
114
115         public ShouldInterceptRequestHelper getShouldInterceptRequestHelper() {
116             return mShouldInterceptRequestHelper;
117         }
118
119         public OnLoadResourceHelper getOnLoadResourceHelper() {
120             return mOnLoadResourceHelper;
121         }
122     }
123
124     private static final int TEAPOT_STATUS_CODE = 418;
125     private static final String TEAPOT_RESPONSE_PHRASE = "I'm a teapot";
126
127     private String addPageToTestServer(TestWebServer webServer, String httpPath, String html) {
128         List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
129         headers.add(Pair.create("Content-Type", "text/html"));
130         headers.add(Pair.create("Cache-Control", "no-store"));
131         return webServer.setResponse(httpPath, html, headers);
132     }
133
134     private String addAboutPageToTestServer(TestWebServer webServer) {
135         return addPageToTestServer(webServer, "/" + CommonResources.ABOUT_FILENAME,
136                 CommonResources.ABOUT_HTML);
137     }
138
139     private AwWebResourceResponse stringToAwWebResourceResponse(String input) throws Throwable {
140         final String mimeType = "text/html";
141         final String encoding = "UTF-8";
142
143         return new AwWebResourceResponse(
144                 mimeType, encoding, new ByteArrayInputStream(input.getBytes(encoding)));
145     }
146
147     private TestWebServer mWebServer;
148     private TestAwContentsClient mContentsClient;
149     private AwTestContainerView mTestContainerView;
150     private AwContents mAwContents;
151     private TestAwContentsClient.ShouldInterceptRequestHelper mShouldInterceptRequestHelper;
152
153     @Override
154     protected void setUp() throws Exception {
155         super.setUp();
156
157         mContentsClient = new TestAwContentsClient();
158         mTestContainerView = createAwTestContainerViewOnMainSync(mContentsClient);
159         mAwContents = mTestContainerView.getAwContents();
160         mShouldInterceptRequestHelper = mContentsClient.getShouldInterceptRequestHelper();
161
162         mWebServer = new TestWebServer(false);
163     }
164
165     @Override
166     protected void tearDown() throws Exception {
167         mWebServer.shutdown();
168         super.tearDown();
169     }
170
171     @SmallTest
172     @Feature({"AndroidWebView"})
173     public void testCalledWithCorrectUrlParam() throws Throwable {
174         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
175
176         int onPageFinishedCallCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
177
178         int callCount = mShouldInterceptRequestHelper.getCallCount();
179         loadUrlAsync(mAwContents, aboutPageUrl);
180         mShouldInterceptRequestHelper.waitForCallback(callCount);
181         assertEquals(1, mShouldInterceptRequestHelper.getUrls().size());
182         assertEquals(aboutPageUrl,
183                 mShouldInterceptRequestHelper.getUrls().get(0));
184
185         mContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
186         assertEquals(CommonResources.ABOUT_TITLE, getTitleOnUiThread(mAwContents));
187     }
188
189     @SmallTest
190     @Feature({"AndroidWebView"})
191     public void testCalledWithCorrectIsMainFrameParam() throws Throwable {
192         final String subframeUrl = addAboutPageToTestServer(mWebServer);
193         final String pageWithIframeUrl = addPageToTestServer(mWebServer, "/page_with_iframe.html",
194                 CommonResources.makeHtmlPageFrom("",
195                     "<iframe src=\"" + subframeUrl + "\"/>"));
196
197         int callCount = mShouldInterceptRequestHelper.getCallCount();
198         loadUrlAsync(mAwContents, pageWithIframeUrl);
199         mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
200         assertEquals(2, mShouldInterceptRequestHelper.getUrls().size());
201         assertEquals(false,
202                 mShouldInterceptRequestHelper.getParamsForUrl(subframeUrl).isMainFrame);
203         assertEquals(true,
204                 mShouldInterceptRequestHelper.getParamsForUrl(pageWithIframeUrl).isMainFrame);
205     }
206
207     @SmallTest
208     @Feature({"AndroidWebView"})
209     public void testCalledWithCorrectMethodParam() throws Throwable {
210         final String pageToPostToUrl = addAboutPageToTestServer(mWebServer);
211         final String pageWithFormUrl = addPageToTestServer(mWebServer, "/page_with_form.html",
212                 CommonResources.makeHtmlPageWithSimplePostFormTo(pageToPostToUrl));
213         enableJavaScriptOnUiThread(mAwContents);
214
215         int callCount = mShouldInterceptRequestHelper.getCallCount();
216         loadUrlAsync(mAwContents, pageWithFormUrl);
217         mShouldInterceptRequestHelper.waitForCallback(callCount);
218         assertEquals("GET",
219                 mShouldInterceptRequestHelper.getParamsForUrl(pageWithFormUrl).method);
220
221         callCount = mShouldInterceptRequestHelper.getCallCount();
222         JSUtils.clickOnLinkUsingJs(this, mAwContents,
223                 mContentsClient.getOnEvaluateJavaScriptResultHelper(), "link");
224         mShouldInterceptRequestHelper.waitForCallback(callCount);
225         assertEquals("POST",
226                 mShouldInterceptRequestHelper.getParamsForUrl(pageToPostToUrl).method);
227     }
228
229     @SmallTest
230     @Feature({"AndroidWebView"})
231     public void testCalledWithCorrectHasUserGestureParam() throws Throwable {
232         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
233         final String pageWithLinkUrl = addPageToTestServer(mWebServer, "/page_with_link.html",
234                 CommonResources.makeHtmlPageWithSimpleLinkTo(aboutPageUrl));
235         enableJavaScriptOnUiThread(mAwContents);
236
237         int callCount = mShouldInterceptRequestHelper.getCallCount();
238         loadUrlAsync(mAwContents, pageWithLinkUrl);
239         mShouldInterceptRequestHelper.waitForCallback(callCount);
240         assertEquals(false,
241                 mShouldInterceptRequestHelper.getParamsForUrl(pageWithLinkUrl).hasUserGesture);
242
243         // TODO(mkosiba): Remove this once we have a real API to wait for the page to load and
244         // display.
245         // http://crbug.com/364612
246         //
247         // The code here is waiting for the "link" (which is a full-screen blue div) to appear on
248         // screen.
249         pollOnUiThread(new Callable<Boolean>() {
250             @Override
251             public Boolean call() throws Exception {
252                 Bitmap bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
253                 Canvas canvas = new Canvas(bitmap);
254                 canvas.translate(-(float) mTestContainerView.getWidth() / 2,
255                         -(float) mTestContainerView.getHeight() / 2);
256                 mAwContents.onDraw(canvas);
257                 return bitmap.getPixel(0, 0) == Color.BLUE;
258             }
259         });
260         callCount = mShouldInterceptRequestHelper.getCallCount();
261         AwTestTouchUtils.simulateTouchCenterOfView(mTestContainerView);
262         mShouldInterceptRequestHelper.waitForCallback(callCount);
263         assertEquals(true,
264                 mShouldInterceptRequestHelper.getParamsForUrl(aboutPageUrl).hasUserGesture);
265     }
266
267     @SmallTest
268     @Feature({"AndroidWebView"})
269     public void testCalledWithCorrectHeadersParam() throws Throwable {
270         final String headerName = "X-Test-Header-Name";
271         final String headerValue = "TestHeaderValue";
272         final String syncGetUrl = addPageToTestServer(mWebServer, "/intercept_me",
273                 CommonResources.ABOUT_HTML);
274         final String mainPageUrl = addPageToTestServer(mWebServer, "/main",
275                 CommonResources.makeHtmlPageFrom("",
276                 "<script>" +
277                 "  var xhr = new XMLHttpRequest();" +
278                 "  xhr.open('GET', '" + syncGetUrl + "', false);" +
279                 "  xhr.setRequestHeader('" + headerName + "', '" + headerValue + "'); " +
280                 "  xhr.send(null);" +
281                 "</script>"));
282         enableJavaScriptOnUiThread(mAwContents);
283
284         int callCount = mShouldInterceptRequestHelper.getCallCount();
285         loadUrlAsync(mAwContents, mainPageUrl);
286         mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
287
288         Map<String, String> headers =
289             mShouldInterceptRequestHelper.getParamsForUrl(syncGetUrl).requestHeaders;
290         assertTrue(headers.containsKey(headerName));
291         assertEquals(headerValue, headers.get(headerName));
292     }
293
294     @SmallTest
295     @Feature({"AndroidWebView"})
296     public void testOnLoadResourceCalledWithCorrectUrl() throws Throwable {
297         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
298         final TestAwContentsClient.OnLoadResourceHelper onLoadResourceHelper =
299             mContentsClient.getOnLoadResourceHelper();
300
301         int callCount = onLoadResourceHelper.getCallCount();
302
303         loadUrlAsync(mAwContents, aboutPageUrl);
304
305         onLoadResourceHelper.waitForCallback(callCount);
306         assertEquals(aboutPageUrl, onLoadResourceHelper.getUrl());
307     }
308
309     @SmallTest
310     @Feature({"AndroidWebView"})
311     public void testDoesNotCrashOnInvalidData() throws Throwable {
312         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
313
314         mShouldInterceptRequestHelper.setReturnValue(
315                 new AwWebResourceResponse("text/html", "UTF-8", null));
316         int callCount = mShouldInterceptRequestHelper.getCallCount();
317         loadUrlAsync(mAwContents, aboutPageUrl);
318         mShouldInterceptRequestHelper.waitForCallback(callCount);
319
320         mShouldInterceptRequestHelper.setReturnValue(
321                 new AwWebResourceResponse(null, null, new ByteArrayInputStream(new byte[0])));
322         callCount = mShouldInterceptRequestHelper.getCallCount();
323         loadUrlAsync(mAwContents, aboutPageUrl);
324         mShouldInterceptRequestHelper.waitForCallback(callCount);
325
326         mShouldInterceptRequestHelper.setReturnValue(
327                 new AwWebResourceResponse(null, null, null));
328         callCount = mShouldInterceptRequestHelper.getCallCount();
329         loadUrlAsync(mAwContents, aboutPageUrl);
330         mShouldInterceptRequestHelper.waitForCallback(callCount);
331     }
332
333     private static class EmptyInputStream extends InputStream {
334         @Override
335         public int available() {
336             return 0;
337         }
338
339         @Override
340         public int read() throws IOException {
341             return -1;
342         }
343
344         @Override
345         public int read(byte b[]) throws IOException {
346             return -1;
347         }
348
349         @Override
350         public int read(byte b[], int off, int len) throws IOException {
351             return -1;
352         }
353
354         @Override
355         public long skip(long n) throws IOException {
356             if (n < 0)
357                 throw new IOException("skipping negative number of bytes");
358             return 0;
359         }
360     }
361
362     @SmallTest
363     @Feature({"AndroidWebView"})
364     public void testDoesNotCrashOnEmptyStream() throws Throwable {
365         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
366
367         mShouldInterceptRequestHelper.setReturnValue(
368                 new AwWebResourceResponse("text/html", "UTF-8", new EmptyInputStream()));
369         int shouldInterceptRequestCallCount = mShouldInterceptRequestHelper.getCallCount();
370         int onPageFinishedCallCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
371
372         loadUrlAsync(mAwContents, aboutPageUrl);
373
374         mShouldInterceptRequestHelper.waitForCallback(shouldInterceptRequestCallCount);
375         mContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
376     }
377
378     private static class ThrowingInputStream extends EmptyInputStream {
379         @Override
380         public int available() {
381             return 100;
382         }
383
384         @Override
385         public int read() throws IOException {
386             throw new IOException("test exception");
387         }
388
389         @Override
390         public int read(byte b[]) throws IOException {
391             throw new IOException("test exception");
392         }
393
394         @Override
395         public int read(byte b[], int off, int len) throws IOException {
396             throw new IOException("test exception");
397         }
398
399         @Override
400         public long skip(long n) throws IOException {
401             return n;
402         }
403     }
404
405     @SmallTest
406     @Feature({"AndroidWebView"})
407     public void testDoesNotCrashOnThrowingStream() throws Throwable {
408         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
409
410         mShouldInterceptRequestHelper.setReturnValue(
411                 new AwWebResourceResponse("text/html", "UTF-8", new ThrowingInputStream()));
412         int shouldInterceptRequestCallCount = mShouldInterceptRequestHelper.getCallCount();
413         int onPageFinishedCallCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
414
415         loadUrlAsync(mAwContents, aboutPageUrl);
416
417         mShouldInterceptRequestHelper.waitForCallback(shouldInterceptRequestCallCount);
418         mContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
419     }
420
421     private static class SlowAwWebResourceResponse extends AwWebResourceResponse {
422         private CallbackHelper mReadStartedCallbackHelper = new CallbackHelper();
423         private CountDownLatch mLatch = new CountDownLatch(1);
424
425         public SlowAwWebResourceResponse(String mimeType, String encoding, InputStream data) {
426             super(mimeType, encoding, data);
427         }
428
429         @Override
430         public InputStream getData() {
431             mReadStartedCallbackHelper.notifyCalled();
432             try {
433                 mLatch.await();
434             } catch (InterruptedException e) {
435                 // ignore
436             }
437             return super.getData();
438         }
439
440         public void unblockReads() {
441             mLatch.countDown();
442         }
443
444         public CallbackHelper getReadStartedCallbackHelper() {
445             return mReadStartedCallbackHelper;
446         }
447     }
448
449     @SmallTest
450     @Feature({"AndroidWebView"})
451     public void testDoesNotCrashOnSlowStream() throws Throwable {
452         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
453         final String aboutPageData = makePageWithTitle("some title");
454         final String encoding = "UTF-8";
455         final SlowAwWebResourceResponse slowAwWebResourceResponse =
456             new SlowAwWebResourceResponse("text/html", encoding,
457                     new ByteArrayInputStream(aboutPageData.getBytes(encoding)));
458
459         mShouldInterceptRequestHelper.setReturnValue(slowAwWebResourceResponse);
460         int callCount = slowAwWebResourceResponse.getReadStartedCallbackHelper().getCallCount();
461         loadUrlAsync(mAwContents, aboutPageUrl);
462         slowAwWebResourceResponse.getReadStartedCallbackHelper().waitForCallback(callCount);
463
464         // Now the AwContents is "stuck" waiting for the SlowInputStream to finish reading so we
465         // delete it to make sure that the dangling 'read' task doesn't cause a crash. Unfortunately
466         // this will not always lead to a crash but it should happen often enough for us to notice.
467
468         runTestOnUiThread(new Runnable() {
469             @Override
470             public void run() {
471                 getActivity().removeAllViews();
472             }
473         });
474         destroyAwContentsOnMainSync(mAwContents);
475         pollOnUiThread(new Callable<Boolean>() {
476             @Override
477             public Boolean call() {
478                 return AwContents.getNativeInstanceCount() == 0;
479             }
480         });
481
482         slowAwWebResourceResponse.unblockReads();
483     }
484
485     @SmallTest
486     @Feature({"AndroidWebView"})
487     public void testHttpStatusCodeAndText() throws Throwable {
488         final String syncGetUrl = mWebServer.getResponseUrl("/intercept_me");
489         final String syncGetJs =
490             "(function() {" +
491             "  var xhr = new XMLHttpRequest();" +
492             "  xhr.open('GET', '" + syncGetUrl + "', false);" +
493             "  xhr.send(null);" +
494             "  console.info('xhr.status = ' + xhr.status);" +
495             "  console.info('xhr.statusText = ' + xhr.statusText);" +
496             "  return '[' + xhr.status + '][' + xhr.statusText + ']';" +
497             "})();";
498         enableJavaScriptOnUiThread(mAwContents);
499
500         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
501         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
502
503         mShouldInterceptRequestHelper.setReturnValue(
504                 new AwWebResourceResponse("text/html", "UTF-8", null));
505         assertEquals("\"[404][Not Found]\"",
506                 executeJavaScriptAndWaitForResult(mAwContents, mContentsClient, syncGetJs));
507
508         mShouldInterceptRequestHelper.setReturnValue(
509                 new AwWebResourceResponse("text/html", "UTF-8", new EmptyInputStream()));
510         assertEquals("\"[200][OK]\"",
511                 executeJavaScriptAndWaitForResult(mAwContents, mContentsClient, syncGetJs));
512
513         mShouldInterceptRequestHelper.setReturnValue(
514                 new AwWebResourceResponse("text/html", "UTF-8", new EmptyInputStream(),
515                     TEAPOT_STATUS_CODE, TEAPOT_RESPONSE_PHRASE, new HashMap<String, String>()));
516         assertEquals("\"[" + TEAPOT_STATUS_CODE + "][" + TEAPOT_RESPONSE_PHRASE + "]\"",
517                 executeJavaScriptAndWaitForResult(mAwContents, mContentsClient, syncGetJs));
518     }
519
520     private String getHeaderValue(AwContents awContents, TestAwContentsClient contentsClient,
521             String url, String headerName) throws Exception {
522         final String syncGetJs =
523             "(function() {" +
524             "  var xhr = new XMLHttpRequest();" +
525             "  xhr.open('GET', '" + url + "', false);" +
526             "  xhr.send(null);" +
527             "  console.info(xhr.getAllResponseHeaders());" +
528             "  return xhr.getResponseHeader('" + headerName + "');" +
529             "})();";
530         String header = executeJavaScriptAndWaitForResult(awContents, contentsClient, syncGetJs);
531
532         if (header.equals("null"))
533             return null;
534         // JSON stringification applied by executeJavaScriptAndWaitForResult adds quotes
535         // around returned strings.
536         assertTrue(header.length() > 2);
537         assertEquals('"', header.charAt(0));
538         assertEquals('"', header.charAt(header.length() - 1));
539         return header.substring(1, header.length() - 1);
540     }
541
542     @SmallTest
543     @Feature({"AndroidWebView"})
544     public void testHttpResponseClientViaHeader() throws Throwable {
545         final String clientResponseHeaderName = "Client-Via";
546         final String clientResponseHeaderValue = "shouldInterceptRequest";
547         final String syncGetUrl = mWebServer.getResponseUrl("/intercept_me");
548         enableJavaScriptOnUiThread(mAwContents);
549
550         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
551         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
552
553         // The response header is set regardless of whether the embedder has provided a
554         // valid resource stream.
555         mShouldInterceptRequestHelper.setReturnValue(
556                 new AwWebResourceResponse("text/html", "UTF-8", null));
557         assertEquals(clientResponseHeaderValue,
558                 getHeaderValue(mAwContents, mContentsClient, syncGetUrl, clientResponseHeaderName));
559         mShouldInterceptRequestHelper.setReturnValue(
560                 new AwWebResourceResponse("text/html", "UTF-8", new EmptyInputStream()));
561         assertEquals(clientResponseHeaderValue,
562                 getHeaderValue(mAwContents, mContentsClient, syncGetUrl, clientResponseHeaderName));
563
564     }
565
566     @SmallTest
567     @Feature({"AndroidWebView"})
568     public void testHttpResponseHeader() throws Throwable {
569         final String clientResponseHeaderName = "X-Test-Header-Name";
570         final String clientResponseHeaderValue = "TestHeaderValue";
571         final String syncGetUrl = mWebServer.getResponseUrl("/intercept_me");
572         final Map<String, String> headers = new HashMap<String, String>();
573         headers.put(clientResponseHeaderName, clientResponseHeaderValue);
574         enableJavaScriptOnUiThread(mAwContents);
575
576         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
577         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
578
579         mShouldInterceptRequestHelper.setReturnValue(
580                 new AwWebResourceResponse("text/html", "UTF-8", null, 0, null, headers));
581         assertEquals(clientResponseHeaderValue,
582                 getHeaderValue(mAwContents, mContentsClient, syncGetUrl, clientResponseHeaderName));
583     }
584
585     @SmallTest
586     @Feature({"AndroidWebView"})
587     public void testNullHttpResponseHeaders() throws Throwable {
588         final String syncGetUrl = mWebServer.getResponseUrl("/intercept_me");
589         enableJavaScriptOnUiThread(mAwContents);
590
591         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
592         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
593
594         mShouldInterceptRequestHelper.setReturnValue(
595                 new AwWebResourceResponse("text/html", "UTF-8", null, 0, null, null));
596         assertEquals(null, getHeaderValue(mAwContents, mContentsClient, syncGetUrl, "Some-Header"));
597     }
598
599     private String makePageWithTitle(String title) {
600         return CommonResources.makeHtmlPageFrom("<title>" + title + "</title>",
601                 "<div> The title is: " + title + " </div>");
602     }
603
604     @SmallTest
605     @Feature({"AndroidWebView"})
606     public void testCanInterceptMainFrame() throws Throwable {
607         final String expectedTitle = "testShouldInterceptRequestCanInterceptMainFrame";
608         final String expectedPage = makePageWithTitle(expectedTitle);
609
610         mShouldInterceptRequestHelper.setReturnValue(
611                 stringToAwWebResourceResponse(expectedPage));
612
613         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
614
615         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
616
617         assertEquals(expectedTitle, getTitleOnUiThread(mAwContents));
618         assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
619     }
620
621     @SmallTest
622     @Feature({"AndroidWebView"})
623     public void testDoesNotChangeReportedUrl() throws Throwable {
624         mShouldInterceptRequestHelper.setReturnValue(
625                 stringToAwWebResourceResponse(makePageWithTitle("some title")));
626
627         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
628
629         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), aboutPageUrl);
630
631         assertEquals(aboutPageUrl, mContentsClient.getOnPageFinishedHelper().getUrl());
632         assertEquals(aboutPageUrl, mContentsClient.getOnPageStartedHelper().getUrl());
633     }
634
635     @SmallTest
636     @Feature({"AndroidWebView"})
637     public void testNullInputStreamCausesErrorForMainFrame() throws Throwable {
638         final OnReceivedErrorHelper onReceivedErrorHelper =
639             mContentsClient.getOnReceivedErrorHelper();
640
641         mShouldInterceptRequestHelper.setReturnValue(
642                 new AwWebResourceResponse("text/html", "UTF-8", null));
643
644         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
645         final int callCount = onReceivedErrorHelper.getCallCount();
646         loadUrlAsync(mAwContents, aboutPageUrl);
647         onReceivedErrorHelper.waitForCallback(callCount);
648         assertEquals(0, mWebServer.getRequestCount("/" + CommonResources.ABOUT_FILENAME));
649     }
650
651     @SmallTest
652     @Feature({"AndroidWebView"})
653     public void testCalledForImage() throws Throwable {
654         final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
655         mWebServer.setResponseBase64(imagePath,
656                 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
657         final String pageWithImage =
658             addPageToTestServer(mWebServer, "/page_with_image.html",
659                     CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
660
661         int callCount = mShouldInterceptRequestHelper.getCallCount();
662         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithImage);
663         mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
664
665         assertEquals(2, mShouldInterceptRequestHelper.getUrls().size());
666         assertTrue(mShouldInterceptRequestHelper.getUrls().get(1).endsWith(
667                 CommonResources.FAVICON_FILENAME));
668     }
669
670     @SmallTest
671     @Feature({"AndroidWebView"})
672     public void testOnReceivedErrorCallback() throws Throwable {
673         mShouldInterceptRequestHelper.setReturnValue(new AwWebResourceResponse(null, null, null));
674         OnReceivedErrorHelper onReceivedErrorHelper = mContentsClient.getOnReceivedErrorHelper();
675         int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
676         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), "foo://bar");
677         onReceivedErrorHelper.waitForCallback(onReceivedErrorHelperCallCount, 1);
678     }
679
680     @SmallTest
681     @Feature({"AndroidWebView"})
682     public void testNoOnReceivedErrorCallback() throws Throwable {
683         final String imagePath = "/" + CommonResources.FAVICON_FILENAME;
684         final String imageUrl = mWebServer.setResponseBase64(imagePath,
685                 CommonResources.FAVICON_DATA_BASE64, CommonResources.getImagePngHeaders(true));
686         final String pageWithImage =
687                 addPageToTestServer(mWebServer, "/page_with_image.html",
688                         CommonResources.getOnImageLoadedHtml(CommonResources.FAVICON_FILENAME));
689         mShouldInterceptRequestHelper.setReturnValueForUrl(
690                 imageUrl, new AwWebResourceResponse(null, null, null));
691         OnReceivedErrorHelper onReceivedErrorHelper = mContentsClient.getOnReceivedErrorHelper();
692         int onReceivedErrorHelperCallCount = onReceivedErrorHelper.getCallCount();
693         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithImage);
694         assertEquals(onReceivedErrorHelperCallCount, onReceivedErrorHelper.getCallCount());
695     }
696
697     @SmallTest
698     @Feature({"AndroidWebView"})
699     public void testCalledForIframe() throws Throwable {
700         final String aboutPageUrl = addAboutPageToTestServer(mWebServer);
701         final String pageWithIframeUrl = addPageToTestServer(mWebServer, "/page_with_iframe.html",
702                 CommonResources.makeHtmlPageFrom("",
703                     "<iframe src=\"" + aboutPageUrl + "\"/>"));
704
705         int callCount = mShouldInterceptRequestHelper.getCallCount();
706         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), pageWithIframeUrl);
707         mShouldInterceptRequestHelper.waitForCallback(callCount, 2);
708         assertEquals(2, mShouldInterceptRequestHelper.getUrls().size());
709         assertEquals(aboutPageUrl, mShouldInterceptRequestHelper.getUrls().get(1));
710     }
711
712     private void calledForUrlTemplate(final String url) throws Exception {
713         int callCount = mShouldInterceptRequestHelper.getCallCount();
714         int onPageStartedCallCount = mContentsClient.getOnPageStartedHelper().getCallCount();
715         loadUrlAsync(mAwContents, url);
716         mShouldInterceptRequestHelper.waitForCallback(callCount);
717         assertEquals(url, mShouldInterceptRequestHelper.getUrls().get(0));
718
719         mContentsClient.getOnPageStartedHelper().waitForCallback(onPageStartedCallCount);
720         assertEquals(onPageStartedCallCount + 1,
721                 mContentsClient.getOnPageStartedHelper().getCallCount());
722     }
723
724     private void notCalledForUrlTemplate(final String url) throws Exception {
725         int callCount = mShouldInterceptRequestHelper.getCallCount();
726         loadUrlSync(mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
727         // The intercepting must happen before onPageFinished. Since the IPC messages from the
728         // renderer should be delivered in order waiting for onPageFinished is sufficient to
729         // 'flush' any pending interception messages.
730         assertEquals(callCount, mShouldInterceptRequestHelper.getCallCount());
731     }
732
733     @SmallTest
734     @Feature({"AndroidWebView"})
735     public void testCalledForUnsupportedSchemes() throws Throwable {
736         calledForUrlTemplate("foobar://resource/1");
737     }
738
739     @SmallTest
740     @Feature({"AndroidWebView"})
741     public void testCalledForNonexistentFiles() throws Throwable {
742         calledForUrlTemplate("file:///somewhere/something");
743     }
744
745     @SmallTest
746     @Feature({"AndroidWebView"})
747     public void testCalledForExistingFiles() throws Throwable {
748         final String tmpDir = getInstrumentation().getTargetContext().getCacheDir().getPath();
749         final String fileName = tmpDir + "/testfile.html";
750         final String title = "existing file title";
751         TestFileUtil.deleteFile(fileName);  // Remove leftover file if any.
752         TestFileUtil.createNewHtmlFile(fileName, title, "");
753         final String existingFileUrl = "file://" + fileName;
754
755         int callCount = mShouldInterceptRequestHelper.getCallCount();
756         int onPageFinishedCallCount = mContentsClient.getOnPageFinishedHelper().getCallCount();
757         loadUrlAsync(mAwContents, existingFileUrl);
758         mShouldInterceptRequestHelper.waitForCallback(callCount);
759         assertEquals(existingFileUrl, mShouldInterceptRequestHelper.getUrls().get(0));
760
761         mContentsClient.getOnPageFinishedHelper().waitForCallback(onPageFinishedCallCount);
762         assertEquals(title, getTitleOnUiThread(mAwContents));
763         assertEquals(onPageFinishedCallCount + 1,
764                 mContentsClient.getOnPageFinishedHelper().getCallCount());
765     }
766
767     @SmallTest
768     @Feature({"AndroidWebView"})
769     public void testNotCalledForExistingResource() throws Throwable {
770         notCalledForUrlTemplate("file:///android_res/raw/resource_file.html");
771     }
772
773     @SmallTest
774     @Feature({"AndroidWebView"})
775     public void testCalledForNonexistentResource() throws Throwable {
776         calledForUrlTemplate("file:///android_res/raw/no_file.html");
777     }
778
779     @SmallTest
780     @Feature({"AndroidWebView"})
781     public void testNotCalledForExistingAsset() throws Throwable {
782         notCalledForUrlTemplate("file:///android_asset/asset_file.html");
783     }
784
785     @SmallTest
786     @Feature({"AndroidWebView"})
787     public void testCalledForNonexistentAsset() throws Throwable {
788         calledForUrlTemplate("file:///android_res/raw/no_file.html");
789     }
790
791     @SmallTest
792     @Feature({"AndroidWebView"})
793     public void testNotCalledForExistingContentUrl() throws Throwable {
794         final String contentResourceName = "target";
795         final String existingContentUrl = TestContentProvider.createContentUrl(contentResourceName);
796
797         notCalledForUrlTemplate(existingContentUrl);
798
799         int contentRequestCount = TestContentProvider.getResourceRequestCount(
800                 getInstrumentation().getTargetContext(), contentResourceName);
801         assertEquals(1, contentRequestCount);
802     }
803
804     @SmallTest
805     @Feature({"AndroidWebView"})
806     public void testCalledForNonexistentContentUrl() throws Throwable {
807         calledForUrlTemplate("content://org.chromium.webview.NoSuchProvider/foo");
808     }
809 }