1 /*
2  * Copyright 2018 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 androidx.webkit;
18 
19 import android.graphics.Bitmap;
20 import android.net.Uri;
21 import android.webkit.WebResourceRequest;
22 import android.webkit.WebResourceResponse;
23 import android.webkit.WebView;
24 import android.webkit.WebViewClient;
25 
26 import androidx.concurrent.futures.ResolvableFuture;
27 import androidx.test.ext.junit.runners.AndroidJUnit4;
28 import androidx.test.filters.LargeTest;
29 import androidx.webkit.test.common.WebViewOnUiThread;
30 import androidx.webkit.test.common.WebkitUtils;
31 
32 import org.jspecify.annotations.NonNull;
33 import org.junit.After;
34 import org.junit.Assert;
35 import org.junit.Assume;
36 import org.junit.Before;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 
40 import java.io.IOException;
41 import java.util.HashMap;
42 import java.util.Map;
43 import java.util.concurrent.ExecutionException;
44 import java.util.concurrent.TimeUnit;
45 import java.util.concurrent.TimeoutException;
46 
47 import okhttp3.mockwebserver.MockResponse;
48 import okhttp3.mockwebserver.MockWebServer;
49 import okhttp3.mockwebserver.RecordedRequest;
50 
51 @LargeTest
52 @RunWith(AndroidJUnit4.class)
53 public class WebViewClientCompatTest {
54     private WebViewOnUiThread mWebViewOnUiThread;
55     private MockWebServer mWebServer;
56 
57     private static final String TEST_URL = "http://www.example.com/";
58 
59     private static final String TEST_SAFE_BROWSING_URL_PREFIX =
60             "chrome://safe-browsing/match?type=";
61     private static final String TEST_SAFE_BROWSING_MALWARE_URL =
62             TEST_SAFE_BROWSING_URL_PREFIX + "malware";
63     private static final String TEST_SAFE_BROWSING_PHISHING_URL =
64             TEST_SAFE_BROWSING_URL_PREFIX + "phishing";
65     private static final String TEST_SAFE_BROWSING_UNWANTED_SOFTWARE_URL =
66             TEST_SAFE_BROWSING_URL_PREFIX + "unwanted";
67     private static final String TEST_SAFE_BROWSING_BILLING_URL =
68             TEST_SAFE_BROWSING_URL_PREFIX + "billing";
69 
70     @Before
setUp()71     public void setUp() {
72         mWebViewOnUiThread = new WebViewOnUiThread();
73     }
74 
75     @After
tearDown()76     public void tearDown() throws IOException {
77         if (mWebViewOnUiThread != null) {
78             mWebViewOnUiThread.cleanUp();
79         }
80         if (mWebServer != null) {
81             mWebServer.shutdown();
82         }
83     }
84 
85     /**
86      * This should remain functionally equivalent to
87      * android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoadingDefault. Modifications to
88      * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
89      */
90     @Test
testShouldOverrideUrlLoadingDefault()91     public void testShouldOverrideUrlLoadingDefault() {
92         // This never calls into chromium, so we don't need to do any feature checks.
93 
94         final MockWebViewClient webViewClient = new MockWebViewClient();
95 
96         // Create any valid WebResourceRequest, the return values don't matter much.
97         final WebResourceRequest resourceRequest = new WebResourceRequest() {
98             @Override
99             public Uri getUrl() {
100                 return Uri.parse(TEST_URL);
101             }
102 
103             @Override
104             public boolean isForMainFrame() {
105                 return false;
106             }
107 
108             @Override
109             public boolean isRedirect() {
110                 return false;
111             }
112 
113             @Override
114             public boolean hasGesture() {
115                 return false;
116             }
117 
118             @Override
119             public String getMethod() {
120                 return "GET";
121             }
122 
123             @Override
124             public Map<String, String> getRequestHeaders() {
125                 return new HashMap<>();
126             }
127         };
128 
129         Assert.assertFalse(webViewClient.shouldOverrideUrlLoading(
130                 mWebViewOnUiThread.getWebViewOnCurrentThread(), resourceRequest));
131     }
132 
133     /**
134      * This should remain functionally equivalent to
135      * android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoading. Modifications to this
136      * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
137      */
138     @Test
testShouldOverrideUrlLoading()139     public void testShouldOverrideUrlLoading() throws Throwable {
140         WebkitUtils.checkFeature(WebViewFeature.SHOULD_OVERRIDE_WITH_REDIRECTS);
141         WebkitUtils.checkFeature(WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT);
142 
143         // We need to pull up a web server (rather than pick a URL and let the navigation fail)
144         // because the network may attempt to rewrite URLs to send us to a captive portal
145         // (http://b/172937423).
146         mWebServer = new MockWebServer();
147         mWebServer.start();
148         String testUrl = mWebServer.url("/some_test_url").toString();
149         String body = "<html>This is a test page</html>";
150         mWebServer.enqueue(new MockResponse().setBody(body));
151 
152         String data = "<html><body>"
153                 + "<a href=\"" + testUrl + "\" id=\"link\">new page</a>"
154                 + "</body></html>";
155         mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
156         final ResolvableFuture<Void> pageFinishedFuture = ResolvableFuture.create();
157         final MockWebViewClient webViewClient = new MockWebViewClient() {
158             @Override
159             public void onPageFinished(WebView view, String url) {
160                 pageFinishedFuture.set(null);
161             }
162         };
163         mWebViewOnUiThread.setWebViewClient(webViewClient);
164         mWebViewOnUiThread.getSettings().setJavaScriptEnabled(true);
165         clickOnLinkUsingJs("link", mWebViewOnUiThread);
166         WebkitUtils.waitForFuture(pageFinishedFuture);
167         Assert.assertEquals(testUrl,
168                 webViewClient.getLastShouldOverrideResourceRequest().getUrl().toString());
169 
170         WebResourceRequest request = webViewClient.getLastShouldOverrideResourceRequest();
171         Assert.assertNotNull(request);
172         Assert.assertTrue(request.isForMainFrame());
173         Assert.assertFalse(WebResourceRequestCompat.isRedirect(request));
174         Assert.assertFalse(request.hasGesture());
175     }
176 
clickOnLinkUsingJs(final String linkId, WebViewOnUiThread webViewOnUiThread)177     private void clickOnLinkUsingJs(final String linkId, WebViewOnUiThread webViewOnUiThread)
178             throws InterruptedException, ExecutionException, TimeoutException {
179         webViewOnUiThread.evaluateJavascriptSync(
180                 "document.getElementById('" + linkId + "').click();"
181                         + "console.log('element with id [" + linkId + "] clicked');");
182     }
183 
184     /**
185      * This should remain functionally equivalent to
186      * android.webkit.cts.WebViewClientTest#testOnReceivedError. Modifications to this test should
187      * be reflected in that test as necessary. See http://go/modifying-webview-cts.
188      */
189     @Test
testOnReceivedError()190     public void testOnReceivedError() throws Exception {
191         WebkitUtils.checkFeature(WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR);
192         WebkitUtils.checkFeature(WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE);
193 
194         final MockWebViewClient webViewClient = new MockWebViewClient();
195         mWebViewOnUiThread.setWebViewClient(webViewClient);
196 
197         String wrongUri = "invalidscheme://some/resource";
198         Assert.assertNull(webViewClient.getOnReceivedResourceError());
199         mWebViewOnUiThread.loadUrlAndWaitForCompletion(wrongUri);
200         Assert.assertNotNull(webViewClient.getOnReceivedResourceError());
201         Assert.assertEquals(WebViewClient.ERROR_UNSUPPORTED_SCHEME,
202                 webViewClient.getOnReceivedResourceError().getErrorCode());
203     }
204 
205     /**
206      * This should remain functionally equivalent to
207      * android.webkit.cts.WebViewClientTest#testOnReceivedErrorForSubresource. Modifications to
208      * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
209      */
210     @Test
testOnReceivedErrorForSubresource()211     public void testOnReceivedErrorForSubresource() throws Exception {
212         WebkitUtils.checkFeature(WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR);
213 
214         final MockWebViewClient webViewClient = new MockWebViewClient();
215         mWebViewOnUiThread.setWebViewClient(webViewClient);
216 
217         Assert.assertNull(webViewClient.getOnReceivedResourceError());
218         String data = "<html>"
219                 + "  <body>"
220                 + "    <img src=\"invalidscheme://some/resource\" />"
221                 + "  </body>"
222                 + "</html>";
223 
224         mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
225         Assert.assertNotNull(webViewClient.getOnReceivedResourceError());
226         Assert.assertEquals(WebViewClient.ERROR_UNSUPPORTED_SCHEME,
227                 webViewClient.getOnReceivedResourceError().getErrorCode());
228     }
229 
230     /**
231      * This should remain functionally equivalent to
232      * android.webkit.cts.WebViewClientTest#testOnReceivedHttpError. Modifications to
233      * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
234      */
235     @Test
testOnReceivedHttpError()236     public void testOnReceivedHttpError() throws Exception {
237         WebkitUtils.checkFeature(WebViewFeature.RECEIVE_HTTP_ERROR);
238 
239         final MockWebViewClient webViewClient = new MockWebViewClient();
240         mWebViewOnUiThread.setWebViewClient(webViewClient);
241         mWebServer = new MockWebServer();
242         mWebServer.start();
243 
244         Assert.assertNull(webViewClient.getOnReceivedHttpError());
245         String url = mWebServer.url("/non_existent_page").toString();
246         mWebServer.enqueue(new MockResponse().setResponseCode(404));
247 
248         mWebViewOnUiThread.loadUrlAndWaitForCompletion(url);
249 
250         RecordedRequest request = mWebServer.takeRequest(WebkitUtils.TEST_TIMEOUT_MS,
251                 TimeUnit.MILLISECONDS);
252         Assert.assertNotNull("The server should receive an HTTP request", request);
253         Assert.assertEquals("/non_existent_page", request.getPath());
254 
255         Assert.assertNotNull(webViewClient.getOnReceivedHttpError());
256         Assert.assertEquals(404,
257                 webViewClient.getOnReceivedHttpError().getStatusCode());
258     }
259 
260     /**
261      * This should remain functionally equivalent to
262      * android.webkit.cts.WebViewClientTest#testOnSafeBrowsingHitBackToSafety. Modifications to
263      * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
264      */
265     @Test
testOnSafeBrowsingHitBackToSafety()266     public void testOnSafeBrowsingHitBackToSafety() throws Throwable {
267         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_HIT);
268         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_ENABLE);
269         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY);
270         WebkitUtils.checkFeature(WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE);
271 
272         final SafeBrowsingBackToSafetyClient backToSafetyWebViewClient =
273                 new SafeBrowsingBackToSafetyClient();
274         mWebViewOnUiThread.setWebViewClient(backToSafetyWebViewClient);
275         WebSettingsCompat.setSafeBrowsingEnabled(mWebViewOnUiThread.getSettings(), true);
276 
277         // Load any page
278         String data = "<html><body>some safe page</body></html>";
279         mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
280         final String originalUrl = mWebViewOnUiThread.getUrl();
281 
282         enableSafeBrowsingAndLoadUnsafePage(backToSafetyWebViewClient);
283 
284         // Back to safety should produce a network error
285         Assert.assertNotNull(backToSafetyWebViewClient.getOnReceivedResourceError());
286         Assert.assertEquals(WebViewClient.ERROR_UNSAFE_RESOURCE,
287                 backToSafetyWebViewClient.getOnReceivedResourceError().getErrorCode());
288 
289         // Check that we actually navigated backward
290         Assert.assertEquals(originalUrl, mWebViewOnUiThread.getUrl());
291     }
292 
293     /**
294      * This should remain functionally equivalent to
295      * android.webkit.cts.WebViewClientTest#testOnSafeBrowsingHitProceed. Modifications to this
296      * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
297      */
298     @Test
testOnSafeBrowsingHitProceed()299     public void testOnSafeBrowsingHitProceed() throws Throwable {
300         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_HIT);
301         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_ENABLE);
302         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED);
303 
304         final SafeBrowsingProceedClient proceedWebViewClient = new SafeBrowsingProceedClient();
305         mWebViewOnUiThread.setWebViewClient(proceedWebViewClient);
306         WebSettingsCompat.setSafeBrowsingEnabled(mWebViewOnUiThread.getSettings(), true);
307 
308         // Load any page
309         String data = "<html><body>some safe page</body></html>";
310         mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
311 
312         enableSafeBrowsingAndLoadUnsafePage(proceedWebViewClient);
313 
314         // Check that we actually proceeded
315         Assert.assertEquals(TEST_SAFE_BROWSING_MALWARE_URL, mWebViewOnUiThread.getUrl());
316     }
317 
testOnSafeBrowsingCode(String expectedUrl, int expectedThreatType)318     private void testOnSafeBrowsingCode(String expectedUrl, int expectedThreatType)
319             throws Throwable {
320         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_HIT);
321         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_ENABLE);
322         WebkitUtils.checkFeature(WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY);
323 
324         // Load any page
325         String data = "<html><body>some safe page</body></html>";
326         mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
327 
328         final SafeBrowsingBackToSafetyClient backToSafetyWebViewClient =
329                 new SafeBrowsingBackToSafetyClient();
330         mWebViewOnUiThread.setWebViewClient(backToSafetyWebViewClient);
331         WebSettingsCompat.setSafeBrowsingEnabled(mWebViewOnUiThread.getSettings(), true);
332 
333         // Note: Safe Browsing depends on user opt-in as well, so we can't assume it's actually
334         // enabled. #getSafeBrowsingEnabled will tell us the true state of whether Safe Browsing is
335         // enabled.
336         if (WebSettingsCompat.getSafeBrowsingEnabled(mWebViewOnUiThread.getSettings())) {
337             mWebViewOnUiThread.loadUrlAndWaitForCompletion(expectedUrl);
338 
339             Assert.assertEquals("Safe Browsing hit is for unexpected URL",
340                     expectedUrl,
341                     backToSafetyWebViewClient.getOnSafeBrowsingHitRequest().getUrl().toString());
342 
343             Assert.assertEquals("Safe Browsing hit has unexpected threat type",
344                     expectedThreatType,
345                     backToSafetyWebViewClient.getOnSafeBrowsingHitThreatType());
346         }
347     }
348 
349     /**
350      * This should remain functionally equivalent to
351      * android.webkit.cts.WebViewClientTest#testOnSafeBrowsingMalwareCode. Modifications to this
352      * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
353      */
354     @Test
testOnSafeBrowsingMalwareCode()355     public void testOnSafeBrowsingMalwareCode() throws Throwable {
356         testOnSafeBrowsingCode(TEST_SAFE_BROWSING_MALWARE_URL,
357                 WebViewClient.SAFE_BROWSING_THREAT_MALWARE);
358     }
359 
360     /**
361      * This should remain functionally equivalent to
362      * android.webkit.cts.WebViewClientTest#testOnSafeBrowsingPhishingCode. Modifications to this
363      * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
364      */
365     @Test
testOnSafeBrowsingPhishingCode()366     public void testOnSafeBrowsingPhishingCode() throws Throwable {
367         testOnSafeBrowsingCode(TEST_SAFE_BROWSING_PHISHING_URL,
368                 WebViewClient.SAFE_BROWSING_THREAT_PHISHING);
369     }
370 
371     /**
372      * This should remain functionally equivalent to
373      * android.webkit.cts.WebViewClientTest#testOnSafeBrowsingUnwantedSoftwareCode. Modifications to
374      * this test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
375      */
376     @Test
testOnSafeBrowsingUnwantedSoftwareCode()377     public void testOnSafeBrowsingUnwantedSoftwareCode() throws Throwable {
378         testOnSafeBrowsingCode(TEST_SAFE_BROWSING_UNWANTED_SOFTWARE_URL,
379                 WebViewClient.SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE);
380     }
381 
382     /**
383      * This should remain functionally equivalent to
384      * android.webkit.cts.WebViewClientTest#testOnSafeBrowsingBillingCode. Modifications to this
385      * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
386      */
387     @Test
testOnSafeBrowsingBillingCode()388     public void testOnSafeBrowsingBillingCode() throws Throwable {
389         testOnSafeBrowsingCode(TEST_SAFE_BROWSING_BILLING_URL,
390                 WebViewClient.SAFE_BROWSING_THREAT_BILLING);
391     }
392 
enableSafeBrowsingAndLoadUnsafePage(SafeBrowsingClient client)393     private void enableSafeBrowsingAndLoadUnsafePage(SafeBrowsingClient client) throws Throwable {
394         // Note: Safe Browsing depends on user opt-in as well, so we can't assume it's actually
395         // enabled. #getSafeBrowsingEnabled will tell us the true state of whether Safe Browsing is
396         // enabled.
397         boolean deviceSupportsSafeBrowsing =
398                 WebSettingsCompat.getSafeBrowsingEnabled(mWebViewOnUiThread.getSettings());
399         final String msg = "The device should support Safe Browsing";
400         Assume.assumeTrue(msg, deviceSupportsSafeBrowsing);
401 
402         Assert.assertNull(client.getOnReceivedResourceError());
403         mWebViewOnUiThread.loadUrlAndWaitForCompletion(TEST_SAFE_BROWSING_MALWARE_URL);
404 
405         Assert.assertEquals(TEST_SAFE_BROWSING_MALWARE_URL,
406                 client.getOnSafeBrowsingHitRequest().getUrl().toString());
407         Assert.assertTrue(
408                 client.getOnSafeBrowsingHitRequest().isForMainFrame());
409     }
410 
411     /**
412      * This should remain functionally equivalent to
413      * android.webkit.cts.WebViewClientTest#testOnPageCommitVisibleCalled. Modifications to this
414      * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
415      */
416     @Test
testOnPageCommitVisibleCalled()417     public void testOnPageCommitVisibleCalled() throws Exception {
418         WebkitUtils.checkFeature(WebViewFeature.VISUAL_STATE_CALLBACK);
419 
420         final ResolvableFuture<String> pageCommitVisibleFuture = ResolvableFuture.create();
421         mWebViewOnUiThread.setWebViewClient(new WebViewClientCompat() {
422             @Override
423             public void onPageCommitVisible(@NonNull WebView view, @NonNull String url) {
424                 pageCommitVisibleFuture.set(url);
425             }
426         });
427 
428         mWebViewOnUiThread.loadUrl("about:blank");
429         Assert.assertEquals("about:blank", WebkitUtils.waitForFuture(pageCommitVisibleFuture));
430     }
431 
432     @Test
testResetClientToCompat()433     public void testResetClientToCompat() throws Exception {
434         WebkitUtils.checkFeature(WebViewFeature.VISUAL_STATE_CALLBACK);
435 
436         final ResolvableFuture<String> pageCommitVisibleFuture = ResolvableFuture.create();
437         WebViewClient nonCompatClient = new WebViewClient() {
438             @Override
439             public void onPageStarted(WebView view, String url, Bitmap bitmap) {
440                 pageCommitVisibleFuture.setException(new IllegalStateException(
441                         "This client should not have callbacks invoked"));
442             }
443         };
444         mWebViewOnUiThread.setWebViewClient(nonCompatClient);
445 
446         WebViewClientCompat compatClient = new WebViewClientCompat() {
447             @Override
448             public void onPageCommitVisible(@NonNull WebView view, @NonNull String url) {
449                 pageCommitVisibleFuture.set(url);
450             }
451         };
452         mWebViewOnUiThread.setWebViewClient(compatClient); // reset to the new client
453         mWebViewOnUiThread.loadUrl("about:blank");
454         Assert.assertEquals("about:blank", WebkitUtils.waitForFuture(pageCommitVisibleFuture));
455     }
456 
457     @Test
testResetClientToRegular()458     public void testResetClientToRegular() throws Exception {
459         WebViewClientCompat compatClient = new WebViewClientCompat() {
460             @Override
461             public void onPageStarted(WebView view, String url, Bitmap bitmap) {
462                 Assert.fail("This client should not have callbacks invoked");
463             }
464         };
465         mWebViewOnUiThread.setWebViewClient(compatClient);
466 
467         final ResolvableFuture<String> pageFinishedFuture = ResolvableFuture.create();
468         WebViewClient nonCompatClient = new WebViewClient() {
469             @Override
470             public void onPageFinished(WebView view, String url) {
471                 pageFinishedFuture.set(url);
472             }
473         };
474         mWebViewOnUiThread.setWebViewClient(nonCompatClient); // reset to the new client
475         mWebViewOnUiThread.loadUrl("about:blank");
476         Assert.assertEquals("about:blank", WebkitUtils.waitForFuture(pageFinishedFuture));
477     }
478 
479     private class MockWebViewClient extends WebViewOnUiThread.WaitForLoadedClient {
480         private boolean mOnPageStartedCalled;
481         private boolean mOnPageFinishedCalled;
482         private boolean mOnLoadResourceCalled;
483         private WebResourceErrorCompat mOnReceivedResourceError;
484         private WebResourceResponse mOnReceivedHttpError;
485         private WebResourceRequest mLastShouldOverrideResourceRequest;
486 
MockWebViewClient()487         MockWebViewClient() {
488             super(mWebViewOnUiThread);
489         }
490 
getOnReceivedResourceError()491         public WebResourceErrorCompat getOnReceivedResourceError() {
492             return mOnReceivedResourceError;
493         }
494 
getOnReceivedHttpError()495         public WebResourceResponse getOnReceivedHttpError() {
496             return mOnReceivedHttpError;
497         }
498 
getLastShouldOverrideResourceRequest()499         public WebResourceRequest getLastShouldOverrideResourceRequest() {
500             return mLastShouldOverrideResourceRequest;
501         }
502 
503         @Override
onPageStarted(WebView view, String url, Bitmap favicon)504         public void onPageStarted(WebView view, String url, Bitmap favicon) {
505             super.onPageStarted(view, url, favicon);
506             mOnPageStartedCalled = true;
507         }
508 
509         @Override
onPageFinished(WebView view, String url)510         public void onPageFinished(WebView view, String url) {
511             super.onPageFinished(view, url);
512             Assert.assertTrue("Expected onPageStarted to be called before onPageFinished",
513                     mOnPageStartedCalled);
514             Assert.assertTrue(
515                     "Expected onLoadResource or onReceivedError to be called before onPageFinished",
516                     mOnLoadResourceCalled || mOnReceivedResourceError != null);
517             mOnPageFinishedCalled = true;
518         }
519 
520         @Override
onLoadResource(WebView view, String url)521         public void onLoadResource(WebView view, String url) {
522             super.onLoadResource(view, url);
523             mOnLoadResourceCalled = true;
524         }
525 
526         @Override
527         @SuppressWarnings("deprecation")
onReceivedError(WebView view, int errorCode, String description, String failingUrl)528         public void onReceivedError(WebView view, int errorCode,
529                 String description, String failingUrl) {
530             // This can be called if a test runs for a WebView which does not support the {@link
531             // WebViewFeature#RECEIVE_WEB_RESOURCE_ERROR} feature.
532         }
533 
534         @Override
onReceivedError(@onNull WebView view, @NonNull WebResourceRequest request, @NonNull WebResourceErrorCompat error)535         public void onReceivedError(@NonNull WebView view, @NonNull WebResourceRequest request,
536                 @NonNull WebResourceErrorCompat error) {
537             mOnReceivedResourceError = error;
538         }
539 
540         @Override
onReceivedHttpError(@onNull WebView view, @NonNull WebResourceRequest request, @NonNull WebResourceResponse errorResponse)541         public void onReceivedHttpError(@NonNull WebView view, @NonNull WebResourceRequest request,
542                 @NonNull WebResourceResponse errorResponse) {
543             super.onReceivedHttpError(view, request, errorResponse);
544             mOnReceivedHttpError = errorResponse;
545         }
546 
547         @Override
548         @SuppressWarnings("deprecation")
shouldOverrideUrlLoading(WebView view, String url)549         public boolean shouldOverrideUrlLoading(WebView view, String url) {
550             // This can be called if a test runs for a WebView which does not support the {@link
551             // WebViewFeature#SHOULD_OVERRIDE_WITH_REDIRECTS} feature.
552             return false;
553         }
554 
555         @Override
shouldOverrideUrlLoading(@onNull WebView view, @NonNull WebResourceRequest request)556         public boolean shouldOverrideUrlLoading(@NonNull WebView view,
557                 @NonNull WebResourceRequest request) {
558             mLastShouldOverrideResourceRequest = request;
559             return false;
560         }
561     }
562 
563     private class SafeBrowsingClient extends MockWebViewClient {
564         private WebResourceRequest mOnSafeBrowsingHitRequest;
565         private int mOnSafeBrowsingHitThreatType;
566 
getOnSafeBrowsingHitRequest()567         public WebResourceRequest getOnSafeBrowsingHitRequest() {
568             return mOnSafeBrowsingHitRequest;
569         }
570 
setOnSafeBrowsingHitRequest(WebResourceRequest request)571         public void setOnSafeBrowsingHitRequest(WebResourceRequest request) {
572             mOnSafeBrowsingHitRequest = request;
573         }
574 
getOnSafeBrowsingHitThreatType()575         public int getOnSafeBrowsingHitThreatType() {
576             return mOnSafeBrowsingHitThreatType;
577         }
578 
setOnSafeBrowsingHitThreatType(int type)579         public void setOnSafeBrowsingHitThreatType(int type) {
580             mOnSafeBrowsingHitThreatType = type;
581         }
582     }
583 
584     private class SafeBrowsingBackToSafetyClient extends SafeBrowsingClient {
585         @Override
onSafeBrowsingHit(@onNull WebView view, @NonNull WebResourceRequest request, int threatType, @NonNull SafeBrowsingResponseCompat response)586         public void onSafeBrowsingHit(@NonNull WebView view, @NonNull WebResourceRequest request,
587                 int threatType, @NonNull SafeBrowsingResponseCompat response) {
588             // Immediately go back to safety to return the network error code
589             setOnSafeBrowsingHitRequest(request);
590             setOnSafeBrowsingHitThreatType(threatType);
591             response.backToSafety(/* report */ true);
592         }
593     }
594 
595     private class SafeBrowsingProceedClient extends SafeBrowsingClient {
596         @Override
onSafeBrowsingHit(@onNull WebView view, @NonNull WebResourceRequest request, int threatType, @NonNull SafeBrowsingResponseCompat response)597         public void onSafeBrowsingHit(@NonNull WebView view, @NonNull WebResourceRequest request,
598                 int threatType, @NonNull SafeBrowsingResponseCompat response) {
599             // Proceed through Safe Browsing warnings
600             setOnSafeBrowsingHitRequest(request);
601             setOnSafeBrowsingHitThreatType(threatType);
602             response.proceed(/* report */ true);
603         }
604     }
605 }
606