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