1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.webkit.cts;
18 
19 import android.cts.util.EvaluateJsResultPollingCheck;
20 import android.cts.util.NullWebViewUtils;
21 import android.cts.util.PollingCheck;
22 import android.graphics.Bitmap;
23 import android.os.Message;
24 import android.test.ActivityInstrumentationTestCase2;
25 import android.view.KeyEvent;
26 import android.view.ViewGroup;
27 import android.webkit.HttpAuthHandler;
28 import android.webkit.ValueCallback;
29 import android.webkit.WebChromeClient;
30 import android.webkit.WebResourceError;
31 import android.webkit.WebResourceRequest;
32 import android.webkit.WebResourceResponse;
33 import android.webkit.WebSettings;
34 import android.webkit.WebView;
35 import android.webkit.WebViewClient;
36 import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
37 import android.util.Pair;
38 
39 import java.io.ByteArrayInputStream;
40 import java.nio.charset.StandardCharsets;
41 import java.util.HashMap;
42 import java.util.Map;
43 import java.util.List;
44 import java.util.ArrayList;
45 
46 public class WebViewClientTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> {
47     private static final long TEST_TIMEOUT = 5000;
48     private static final String TEST_URL = "http://www.example.com/";
49 
50     private WebViewOnUiThread mOnUiThread;
51     private CtsTestServer mWebServer;
52 
WebViewClientTest()53     public WebViewClientTest() {
54         super("android.webkit.cts", WebViewCtsActivity.class);
55     }
56 
57     @Override
setUp()58     protected void setUp() throws Exception {
59         super.setUp();
60         final WebViewCtsActivity activity = getActivity();
61         WebView webview = activity.getWebView();
62         if (webview != null) {
63             new PollingCheck(TEST_TIMEOUT) {
64                 @Override
65                     protected boolean check() {
66                     return activity.hasWindowFocus();
67                 }
68             }.run();
69 
70             mOnUiThread = new WebViewOnUiThread(this, webview);
71         }
72     }
73 
74     @Override
tearDown()75     protected void tearDown() throws Exception {
76         if (mOnUiThread != null) {
77             mOnUiThread.cleanUp();
78         }
79         if (mWebServer != null) {
80             mWebServer.shutdown();
81         }
82         super.tearDown();
83     }
84 
85     // Verify that the shouldoverrideurlloading is false by default
testShouldOverrideUrlLoadingDefault()86     public void testShouldOverrideUrlLoadingDefault() {
87         if (!NullWebViewUtils.isWebViewAvailable()) {
88             return;
89         }
90         final WebViewClient webViewClient = new WebViewClient();
91         assertFalse(webViewClient.shouldOverrideUrlLoading(mOnUiThread.getWebView(), new String()));
92     }
93 
94     // Verify shouldoverrideurlloading called on top level navigation
testShouldOverrideUrlLoading()95     public void testShouldOverrideUrlLoading() {
96         if (!NullWebViewUtils.isWebViewAvailable()) {
97             return;
98         }
99         final MockWebViewClient webViewClient = new MockWebViewClient();
100         mOnUiThread.setWebViewClient(webViewClient);
101         mOnUiThread.getSettings().setJavaScriptEnabled(true);
102         String data = "<html><body>" +
103                 "<a href=\"" + TEST_URL + "\" id=\"link\">new page</a>" +
104                 "</body></html>";
105         mOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
106         clickOnLinkUsingJs("link", mOnUiThread);
107         assertEquals(TEST_URL, webViewClient.getLastShouldOverrideUrl());
108         assertNotNull(webViewClient.getLastShouldOverrideResourceRequest());
109         assertTrue(webViewClient.getLastShouldOverrideResourceRequest().isForMainFrame());
110         assertFalse(webViewClient.getLastShouldOverrideResourceRequest().isRedirect());
111         assertFalse(webViewClient.getLastShouldOverrideResourceRequest().hasGesture());
112     }
113 
114     // Verify shouldoverrideurlloading called on webview called via onCreateWindow
115     // TODO(sgurun) upstream this test to Aw.
testShouldOverrideUrlLoadingOnCreateWindow()116     public void testShouldOverrideUrlLoadingOnCreateWindow() throws Exception {
117         if (!NullWebViewUtils.isWebViewAvailable()) {
118             return;
119         }
120         mWebServer = new CtsTestServer(getActivity());
121         // WebViewClient for main window
122         final MockWebViewClient mainWebViewClient = new MockWebViewClient();
123         // WebViewClient for child window
124         final MockWebViewClient childWebViewClient = new MockWebViewClient();
125         mOnUiThread.setWebViewClient(mainWebViewClient);
126         mOnUiThread.getSettings().setJavaScriptEnabled(true);
127         mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
128         mOnUiThread.getSettings().setSupportMultipleWindows(true);
129 
130         final WebView childWebView = mOnUiThread.createWebView();
131 
132         mOnUiThread.setWebChromeClient(new WebChromeClient() {
133             @Override
134             public boolean onCreateWindow(
135                 WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
136                 WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
137                 childWebView.setWebViewClient(childWebViewClient);
138                 childWebView.getSettings().setJavaScriptEnabled(true);
139                 transport.setWebView(childWebView);
140                 getActivity().addContentView(childWebView, new ViewGroup.LayoutParams(
141                             ViewGroup.LayoutParams.FILL_PARENT,
142                             ViewGroup.LayoutParams.WRAP_CONTENT));
143                 resultMsg.sendToTarget();
144                 return true;
145             }
146         });
147         mOnUiThread.loadUrl(mWebServer.getAssetUrl(TestHtmlConstants.BLANK_TAG_URL));
148 
149         new PollingCheck(TEST_TIMEOUT) {
150             @Override
151             protected boolean check() {
152                 return childWebViewClient.hasOnPageFinishedCalled();
153             }
154         }.run();
155         assertEquals(mWebServer.getAssetUrl(TestHtmlConstants.PAGE_WITH_LINK_URL),
156                 childWebViewClient.getLastShouldOverrideUrl());
157 
158         // Now test a navigation within the page
159         //TODO(hush) Enable this portion when b/12804986 is fixed.
160         /*
161         WebViewOnUiThread childWebViewOnUiThread = new WebViewOnUiThread(this, childWebView);
162         final int childCallCount = childWebViewClient.getShouldOverrideUrlLoadingCallCount();
163         final int mainCallCount = mainWebViewClient.getShouldOverrideUrlLoadingCallCount();
164         clickOnLinkUsingJs("link", childWebViewOnUiThread);
165         new PollingCheck(TEST_TIMEOUT) {
166             @Override
167             protected boolean check() {
168                 return childWebViewClient.getShouldOverrideUrlLoadingCallCount() > childCallCount;
169             }
170         }.run();
171         assertEquals(mainCallCount, mainWebViewClient.getShouldOverrideUrlLoadingCallCount());
172         assertEquals(TEST_URL, childWebViewClient.getLastShouldOverrideUrl());
173         */
174     }
175 
clickOnLinkUsingJs(final String linkId, WebViewOnUiThread webViewOnUiThread)176     private void clickOnLinkUsingJs(final String linkId, WebViewOnUiThread webViewOnUiThread) {
177         EvaluateJsResultPollingCheck jsResult = new EvaluateJsResultPollingCheck("null");
178         webViewOnUiThread.evaluateJavascript(
179                 "document.getElementById('" + linkId + "').click();" +
180                 "console.log('element with id [" + linkId + "] clicked');", jsResult);
181         jsResult.run();
182     }
183 
testLoadPage()184     public void testLoadPage() throws Exception {
185         if (!NullWebViewUtils.isWebViewAvailable()) {
186             return;
187         }
188         final MockWebViewClient webViewClient = new MockWebViewClient();
189         mOnUiThread.setWebViewClient(webViewClient);
190         mWebServer = new CtsTestServer(getActivity());
191         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
192 
193         assertFalse(webViewClient.hasOnPageStartedCalled());
194         assertFalse(webViewClient.hasOnLoadResourceCalled());
195         assertFalse(webViewClient.hasOnPageFinishedCalled());
196         mOnUiThread.loadUrlAndWaitForCompletion(url);
197 
198         new PollingCheck(TEST_TIMEOUT) {
199             @Override
200             protected boolean check() {
201                 return webViewClient.hasOnPageStartedCalled();
202             }
203         }.run();
204 
205         new PollingCheck(TEST_TIMEOUT) {
206             @Override
207             protected boolean check() {
208                 return webViewClient.hasOnLoadResourceCalled();
209             }
210         }.run();
211 
212         new PollingCheck(TEST_TIMEOUT) {
213             @Override
214             protected boolean check() {
215                 return webViewClient.hasOnPageFinishedCalled();
216             }
217         }.run();
218     }
219 
testOnReceivedLoginRequest()220     public void testOnReceivedLoginRequest() throws Exception {
221         if (!NullWebViewUtils.isWebViewAvailable()) {
222             return;
223         }
224         final MockWebViewClient webViewClient = new MockWebViewClient();
225         mOnUiThread.setWebViewClient(webViewClient);
226         TestWebServer testServer = null;
227         //set the url and html
228         final String path = "/main";
229         final String page = "<head></head><body>test onReceivedLoginRequest</body>";
230         final String headerName = "x-auto-login";
231         final String headerValue = "realm=com.google&account=foo%40bar.com&args=random_string";
232         List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
233         headers.add(Pair.create(headerName, headerValue));
234 
235         try {
236             testServer = new TestWebServer(false);
237             String url = testServer.setResponse(path, page, headers);
238             assertFalse(webViewClient.hasOnReceivedLoginRequest());
239             mOnUiThread.loadUrlAndWaitForCompletion(url);
240             assertTrue(webViewClient.hasOnReceivedLoginRequest());
241             new PollingCheck(TEST_TIMEOUT) {
242                 @Override
243                 protected boolean check() {
244                     return webViewClient.hasOnReceivedLoginRequest();
245                 }
246             }.run();
247            assertEquals("com.google", webViewClient.getLoginRequestRealm());
248            assertEquals("foo@bar.com", webViewClient.getLoginRequestAccount());
249            assertEquals("random_string", webViewClient.getLoginRequestArgs());
250         } finally {
251             testServer.shutdown();
252         }
253     }
testOnReceivedError()254     public void testOnReceivedError() throws Exception {
255         if (!NullWebViewUtils.isWebViewAvailable()) {
256             return;
257         }
258         final MockWebViewClient webViewClient = new MockWebViewClient();
259         mOnUiThread.setWebViewClient(webViewClient);
260 
261         String wrongUri = "invalidscheme://some/resource";
262         assertEquals(0, webViewClient.hasOnReceivedErrorCode());
263         mOnUiThread.loadUrlAndWaitForCompletion(wrongUri);
264         assertEquals(WebViewClient.ERROR_UNSUPPORTED_SCHEME,
265                 webViewClient.hasOnReceivedErrorCode());
266     }
267 
testOnReceivedErrorForSubresource()268     public void testOnReceivedErrorForSubresource() throws Exception {
269         if (!NullWebViewUtils.isWebViewAvailable()) {
270             return;
271         }
272         final MockWebViewClient webViewClient = new MockWebViewClient();
273         mOnUiThread.setWebViewClient(webViewClient);
274         mWebServer = new CtsTestServer(getActivity());
275 
276         assertEquals(null, webViewClient.hasOnReceivedResourceError());
277         String url = mWebServer.getAssetUrl(TestHtmlConstants.BAD_IMAGE_PAGE_URL);
278         mOnUiThread.loadUrlAndWaitForCompletion(url);
279         assertTrue(webViewClient.hasOnReceivedResourceError() != null);
280         assertEquals(WebViewClient.ERROR_UNSUPPORTED_SCHEME,
281                 webViewClient.hasOnReceivedResourceError().getErrorCode());
282     }
283 
testOnReceivedHttpError()284     public void testOnReceivedHttpError() throws Exception {
285         if (!NullWebViewUtils.isWebViewAvailable()) {
286             return;
287         }
288         final MockWebViewClient webViewClient = new MockWebViewClient();
289         mOnUiThread.setWebViewClient(webViewClient);
290         mWebServer = new CtsTestServer(getActivity());
291 
292         assertEquals(null, webViewClient.hasOnReceivedHttpError());
293         String url = mWebServer.getAssetUrl(TestHtmlConstants.NON_EXISTENT_PAGE_URL);
294         mOnUiThread.loadUrlAndWaitForCompletion(url);
295         assertTrue(webViewClient.hasOnReceivedHttpError() != null);
296         assertEquals(404, webViewClient.hasOnReceivedHttpError().getStatusCode());
297     }
298 
testOnFormResubmission()299     public void testOnFormResubmission() throws Exception {
300         if (!NullWebViewUtils.isWebViewAvailable()) {
301             return;
302         }
303         final MockWebViewClient webViewClient = new MockWebViewClient();
304         mOnUiThread.setWebViewClient(webViewClient);
305         final WebSettings settings = mOnUiThread.getSettings();
306         settings.setJavaScriptEnabled(true);
307         mWebServer = new CtsTestServer(getActivity());
308 
309         assertFalse(webViewClient.hasOnFormResubmissionCalled());
310         String url = mWebServer.getAssetUrl(TestHtmlConstants.JS_FORM_URL);
311         // this loads a form, which automatically posts itself
312         mOnUiThread.loadUrlAndWaitForCompletion(url);
313         // wait for JavaScript to post the form
314         mOnUiThread.waitForLoadCompletion();
315         // the URL should have changed when the form was posted
316         assertFalse(url.equals(mOnUiThread.getUrl()));
317         // reloading the current URL should trigger the callback
318         mOnUiThread.reload();
319         new PollingCheck(TEST_TIMEOUT) {
320             @Override
321             protected boolean check() {
322                 return webViewClient.hasOnFormResubmissionCalled();
323             }
324         }.run();
325     }
326 
testDoUpdateVisitedHistory()327     public void testDoUpdateVisitedHistory() throws Exception {
328         if (!NullWebViewUtils.isWebViewAvailable()) {
329             return;
330         }
331         final MockWebViewClient webViewClient = new MockWebViewClient();
332         mOnUiThread.setWebViewClient(webViewClient);
333         mWebServer = new CtsTestServer(getActivity());
334 
335         assertFalse(webViewClient.hasDoUpdateVisitedHistoryCalled());
336         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
337         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.BR_TAG_URL);
338         mOnUiThread.loadUrlAndWaitForCompletion(url1);
339         mOnUiThread.loadUrlAndWaitForCompletion(url2);
340         new PollingCheck(TEST_TIMEOUT) {
341             @Override
342             protected boolean check() {
343                 return webViewClient.hasDoUpdateVisitedHistoryCalled();
344             }
345         }.run();
346     }
347 
testOnReceivedHttpAuthRequest()348     public void testOnReceivedHttpAuthRequest() throws Exception {
349         if (!NullWebViewUtils.isWebViewAvailable()) {
350             return;
351         }
352         final MockWebViewClient webViewClient = new MockWebViewClient();
353         mOnUiThread.setWebViewClient(webViewClient);
354         mWebServer = new CtsTestServer(getActivity());
355 
356         assertFalse(webViewClient.hasOnReceivedHttpAuthRequestCalled());
357         String url = mWebServer.getAuthAssetUrl(TestHtmlConstants.EMBEDDED_IMG_URL);
358         mOnUiThread.loadUrlAndWaitForCompletion(url);
359         assertTrue(webViewClient.hasOnReceivedHttpAuthRequestCalled());
360     }
361 
testShouldOverrideKeyEvent()362     public void testShouldOverrideKeyEvent() {
363         if (!NullWebViewUtils.isWebViewAvailable()) {
364             return;
365         }
366         final MockWebViewClient webViewClient = new MockWebViewClient();
367         mOnUiThread.setWebViewClient(webViewClient);
368 
369         assertFalse(webViewClient.shouldOverrideKeyEvent(mOnUiThread.getWebView(), null));
370     }
371 
testOnUnhandledKeyEvent()372     public void testOnUnhandledKeyEvent() throws Throwable {
373         if (!NullWebViewUtils.isWebViewAvailable()) {
374             return;
375         }
376         requireLoadedPage();
377         final MockWebViewClient webViewClient = new MockWebViewClient();
378         mOnUiThread.setWebViewClient(webViewClient);
379 
380         mOnUiThread.requestFocus();
381         getInstrumentation().waitForIdleSync();
382 
383         assertFalse(webViewClient.hasOnUnhandledKeyEventCalled());
384         sendKeys(KeyEvent.KEYCODE_1);
385 
386         new PollingCheck(TEST_TIMEOUT) {
387             @Override
388             protected boolean check() {
389                 return webViewClient.hasOnUnhandledKeyEventCalled();
390             }
391         }.run();
392     }
393 
testOnScaleChanged()394     public void testOnScaleChanged() throws Throwable {
395         if (!NullWebViewUtils.isWebViewAvailable()) {
396             return;
397         }
398         final MockWebViewClient webViewClient = new MockWebViewClient();
399         mOnUiThread.setWebViewClient(webViewClient);
400         mWebServer = new CtsTestServer(getActivity());
401 
402         assertFalse(webViewClient.hasOnScaleChangedCalled());
403         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
404         mOnUiThread.loadUrlAndWaitForCompletion(url1);
405 
406         new PollingCheck(TEST_TIMEOUT) {
407             @Override
408             protected boolean check() {
409                 return mOnUiThread.canZoomIn();
410             }
411         }.run();
412 
413         assertTrue(mOnUiThread.zoomIn());
414         new PollingCheck(TEST_TIMEOUT) {
415             @Override
416             protected boolean check() {
417                 return webViewClient.hasOnScaleChangedCalled();
418             }
419         }.run();
420     }
421 
422     // Test that shouldInterceptRequest is called with the correct parameters
testShouldInterceptRequestParams()423     public void testShouldInterceptRequestParams() throws Throwable {
424         if (!NullWebViewUtils.isWebViewAvailable()) {
425             return;
426         }
427 
428         final String mainPath = "/main";
429         final String mainPage = "<head></head><body>test page</body>";
430         final String headerName = "x-test-header-name";
431         final String headerValue = "testheadervalue";
432         HashMap<String, String> headers = new HashMap<String, String>(1);
433         headers.put(headerName, headerValue);
434 
435         // A client which saves the WebResourceRequest as interceptRequest
436         final class TestClient extends WaitForLoadedClient {
437             public TestClient() {
438                 super(mOnUiThread);
439             }
440 
441             @Override
442             public WebResourceResponse shouldInterceptRequest(WebView view,
443                     WebResourceRequest request) {
444                 assertNotNull(view);
445                 assertNotNull(request);
446 
447                 assertEquals(view, mOnUiThread.getWebView());
448 
449                 // Save the main page request; discard any other requests (e.g. for favicon.ico)
450                 if (request.getUrl().getPath().equals(mainPath)) {
451                     assertNull(interceptRequest);
452                     interceptRequest = request;
453                 }
454 
455                 return null;
456             }
457 
458             public volatile WebResourceRequest interceptRequest;
459         }
460 
461         TestClient client = new TestClient();
462         mOnUiThread.setWebViewClient(client);
463 
464         TestWebServer server = new TestWebServer(false);
465         try {
466             String mainUrl = server.setResponse(mainPath, mainPage, null);
467 
468             mOnUiThread.loadUrlAndWaitForCompletion(mainUrl, headers);
469 
470             // Inspect the fields of the saved WebResourceRequest
471             assertNotNull(client.interceptRequest);
472             assertEquals(mainUrl, client.interceptRequest.getUrl().toString());
473             assertTrue(client.interceptRequest.isForMainFrame());
474             assertEquals(server.getLastRequest(mainPath).getRequestLine().getMethod(),
475                 client.interceptRequest.getMethod());
476 
477             // Web request headers are case-insensitive. We provided lower-case headerName and
478             // headerValue. This will pass implementations which either do not mangle case,
479             // convert to lowercase, or convert to uppercase but return a case-insensitive map.
480             Map<String, String> interceptHeaders = client.interceptRequest.getRequestHeaders();
481             assertTrue(interceptHeaders.containsKey(headerName));
482             assertEquals(headerValue, interceptHeaders.get(headerName));
483         } finally {
484             server.shutdown();
485         }
486     }
487 
488     // Test that the WebResourceResponse returned by shouldInterceptRequest is handled correctly
testShouldInterceptRequestResponse()489     public void testShouldInterceptRequestResponse() throws Throwable {
490         if (!NullWebViewUtils.isWebViewAvailable()) {
491             return;
492         }
493 
494         final String mainPath = "/main";
495         final String mainPage = "<head></head><body>test page</body>";
496         final String interceptPath = "/intercept_me";
497 
498         // A client which responds to requests for interceptPath with a saved interceptResponse
499         final class TestClient extends WaitForLoadedClient {
500             public TestClient() {
501                 super(mOnUiThread);
502             }
503 
504             @Override
505             public WebResourceResponse shouldInterceptRequest(WebView view,
506                     WebResourceRequest request) {
507                 if (request.getUrl().toString().contains(interceptPath)) {
508                     assertNotNull(interceptResponse);
509                     return interceptResponse;
510                 }
511 
512                 return null;
513             }
514 
515             volatile public WebResourceResponse interceptResponse;
516         }
517 
518         mOnUiThread.getSettings().setJavaScriptEnabled(true);
519 
520         TestClient client = new TestClient();
521         mOnUiThread.setWebViewClient(client);
522 
523         TestWebServer server = new TestWebServer(false);
524         try {
525             String interceptUrl = server.getResponseUrl(interceptPath);
526 
527             // JavaScript which makes a synchronous AJAX request and logs and returns the status
528             String js =
529                 "(function() {" +
530                 "  var xhr = new XMLHttpRequest();" +
531                 "  xhr.open('GET', '" + interceptUrl + "', false);" +
532                 "  xhr.send(null);" +
533                 "  console.info('xhr.status = ' + xhr.status);" +
534                 "  console.info('xhr.statusText = ' + xhr.statusText);" +
535                 "  return '[' + xhr.status + '][' + xhr.statusText + ']';" +
536                 "})();";
537 
538             String mainUrl = server.setResponse(mainPath, mainPage, null);
539             mOnUiThread.loadUrlAndWaitForCompletion(mainUrl, null);
540 
541             EvaluateJsResultPollingCheck jsResult;
542 
543             // Test a nonexistent page
544             client.interceptResponse = new WebResourceResponse("text/html", "UTF-8", null);
545             jsResult = new EvaluateJsResultPollingCheck("\"[404][Not Found]\"");
546             mOnUiThread.evaluateJavascript(js, jsResult);
547             jsResult.run();
548 
549             // Test an empty page
550             client.interceptResponse = new WebResourceResponse("text/html", "UTF-8",
551                 new ByteArrayInputStream(new byte[0]));
552             jsResult = new EvaluateJsResultPollingCheck("\"[200][OK]\"");
553             mOnUiThread.evaluateJavascript(js, jsResult);
554             jsResult.run();
555 
556             // Test a nonempty page with unusual response code/text
557             client.interceptResponse =
558                 new WebResourceResponse("text/html", "UTF-8", 123, "unusual", null,
559                     new ByteArrayInputStream("nonempty page".getBytes(StandardCharsets.UTF_8)));
560             jsResult = new EvaluateJsResultPollingCheck("\"[123][unusual]\"");
561             mOnUiThread.evaluateJavascript(js, jsResult);
562             jsResult.run();
563         } finally {
564             server.shutdown();
565         }
566     }
567 
requireLoadedPage()568     private void requireLoadedPage() throws Throwable {
569         if (!NullWebViewUtils.isWebViewAvailable()) {
570             return;
571         }
572         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
573     }
574 
575     private class MockWebViewClient extends WaitForLoadedClient {
576         private boolean mOnPageStartedCalled;
577         private boolean mOnPageFinishedCalled;
578         private boolean mOnLoadResourceCalled;
579         private int mOnReceivedErrorCode;
580         private WebResourceError mOnReceivedResourceError;
581         private WebResourceResponse mOnReceivedHttpError;
582         private boolean mOnFormResubmissionCalled;
583         private boolean mDoUpdateVisitedHistoryCalled;
584         private boolean mOnReceivedHttpAuthRequestCalled;
585         private boolean mOnReceivedLoginRequest;
586         private String mOnReceivedLoginAccount;
587         private String mOnReceivedLoginArgs;
588         private String mOnReceivedLoginRealm;
589         private boolean mOnUnhandledKeyEventCalled;
590         private boolean mOnScaleChangedCalled;
591         private int mShouldOverrideUrlLoadingCallCount;
592         private String mLastShouldOverrideUrl;
593         private WebResourceRequest mLastShouldOverrideResourceRequest;
594 
MockWebViewClient()595         public MockWebViewClient() {
596             super(mOnUiThread);
597         }
598 
hasOnPageStartedCalled()599         public boolean hasOnPageStartedCalled() {
600             return mOnPageStartedCalled;
601         }
602 
hasOnPageFinishedCalled()603         public boolean hasOnPageFinishedCalled() {
604             return mOnPageFinishedCalled;
605         }
606 
hasOnLoadResourceCalled()607         public boolean hasOnLoadResourceCalled() {
608             return mOnLoadResourceCalled;
609         }
610 
hasOnReceivedErrorCode()611         public int hasOnReceivedErrorCode() {
612             return mOnReceivedErrorCode;
613         }
614 
hasOnReceivedLoginRequest()615         public boolean hasOnReceivedLoginRequest() {
616             return mOnReceivedLoginRequest;
617         }
618 
hasOnReceivedResourceError()619         public WebResourceError hasOnReceivedResourceError() {
620             return mOnReceivedResourceError;
621         }
622 
hasOnReceivedHttpError()623         public WebResourceResponse hasOnReceivedHttpError() {
624             return mOnReceivedHttpError;
625         }
626 
hasOnFormResubmissionCalled()627         public boolean hasOnFormResubmissionCalled() {
628             return mOnFormResubmissionCalled;
629         }
630 
hasDoUpdateVisitedHistoryCalled()631         public boolean hasDoUpdateVisitedHistoryCalled() {
632             return mDoUpdateVisitedHistoryCalled;
633         }
634 
hasOnReceivedHttpAuthRequestCalled()635         public boolean hasOnReceivedHttpAuthRequestCalled() {
636             return mOnReceivedHttpAuthRequestCalled;
637         }
638 
hasOnUnhandledKeyEventCalled()639         public boolean hasOnUnhandledKeyEventCalled() {
640             return mOnUnhandledKeyEventCalled;
641         }
642 
hasOnScaleChangedCalled()643         public boolean hasOnScaleChangedCalled() {
644             return mOnScaleChangedCalled;
645         }
646 
getShouldOverrideUrlLoadingCallCount()647         public int getShouldOverrideUrlLoadingCallCount() {
648             return mShouldOverrideUrlLoadingCallCount;
649         }
650 
getLastShouldOverrideUrl()651         public String getLastShouldOverrideUrl() {
652             return mLastShouldOverrideUrl;
653         }
654 
getLastShouldOverrideResourceRequest()655         public WebResourceRequest getLastShouldOverrideResourceRequest() {
656             return mLastShouldOverrideResourceRequest;
657         }
658 
getLoginRequestRealm()659         public String getLoginRequestRealm() {
660             return mOnReceivedLoginRealm;
661         }
662 
getLoginRequestAccount()663         public String getLoginRequestAccount() {
664             return mOnReceivedLoginAccount;
665         }
666 
getLoginRequestArgs()667         public String getLoginRequestArgs() {
668             return mOnReceivedLoginArgs;
669         }
670 
671         @Override
onPageStarted(WebView view, String url, Bitmap favicon)672         public void onPageStarted(WebView view, String url, Bitmap favicon) {
673             super.onPageStarted(view, url, favicon);
674             mOnPageStartedCalled = true;
675         }
676 
677         @Override
onPageFinished(WebView view, String url)678         public void onPageFinished(WebView view, String url) {
679             super.onPageFinished(view, url);
680             assertTrue(mOnPageStartedCalled);
681             assertTrue(mOnLoadResourceCalled);
682             mOnPageFinishedCalled = true;
683         }
684 
685         @Override
onLoadResource(WebView view, String url)686         public void onLoadResource(WebView view, String url) {
687             super.onLoadResource(view, url);
688             assertTrue(mOnPageStartedCalled);
689             mOnLoadResourceCalled = true;
690         }
691 
692         @Override
onReceivedError(WebView view, int errorCode, String description, String failingUrl)693         public void onReceivedError(WebView view, int errorCode,
694                 String description, String failingUrl) {
695             super.onReceivedError(view, errorCode, description, failingUrl);
696             mOnReceivedErrorCode = errorCode;
697         }
698 
699         @Override
onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)700         public void onReceivedError(WebView view, WebResourceRequest request,
701                 WebResourceError error) {
702             super.onReceivedError(view, request, error);
703             mOnReceivedResourceError = error;
704         }
705 
706         @Override
onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse)707         public void onReceivedHttpError(WebView view,  WebResourceRequest request,
708                 WebResourceResponse errorResponse) {
709             super.onReceivedHttpError(view, request, errorResponse);
710             mOnReceivedHttpError = errorResponse;
711         }
712 
713         @Override
onReceivedLoginRequest(WebView view, String realm, String account, String args)714         public void onReceivedLoginRequest(WebView view, String realm, String account,
715                 String args) {
716             super.onReceivedLoginRequest(view, realm, account, args);
717             mOnReceivedLoginRequest = true;
718             mOnReceivedLoginRealm = realm;
719             mOnReceivedLoginAccount = account;
720             mOnReceivedLoginArgs = args;
721        }
722 
723         @Override
onFormResubmission(WebView view, Message dontResend, Message resend)724         public void onFormResubmission(WebView view, Message dontResend, Message resend) {
725             mOnFormResubmissionCalled = true;
726             dontResend.sendToTarget();
727         }
728 
729         @Override
doUpdateVisitedHistory(WebView view, String url, boolean isReload)730         public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
731             super.doUpdateVisitedHistory(view, url, isReload);
732             mDoUpdateVisitedHistoryCalled = true;
733         }
734 
735         @Override
onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)736         public void onReceivedHttpAuthRequest(WebView view,
737                 HttpAuthHandler handler, String host, String realm) {
738             super.onReceivedHttpAuthRequest(view, handler, host, realm);
739             mOnReceivedHttpAuthRequestCalled = true;
740         }
741 
742         @Override
onUnhandledKeyEvent(WebView view, KeyEvent event)743         public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
744             super.onUnhandledKeyEvent(view, event);
745             mOnUnhandledKeyEventCalled = true;
746         }
747 
748         @Override
onScaleChanged(WebView view, float oldScale, float newScale)749         public void onScaleChanged(WebView view, float oldScale, float newScale) {
750             super.onScaleChanged(view, oldScale, newScale);
751             mOnScaleChangedCalled = true;
752         }
753 
754         @Override
shouldOverrideUrlLoading(WebView view, String url)755         public boolean shouldOverrideUrlLoading(WebView view, String url) {
756             mLastShouldOverrideUrl = url;
757             mLastShouldOverrideResourceRequest = null;
758             mShouldOverrideUrlLoadingCallCount++;
759             return false;
760         }
761 
762         @Override
shouldOverrideUrlLoading(WebView view, WebResourceRequest request)763         public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
764             mLastShouldOverrideUrl = request.getUrl().toString();
765             mLastShouldOverrideResourceRequest = request;
766             mShouldOverrideUrlLoadingCallCount++;
767             return false;
768         }
769     }
770 }
771