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