1e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer/*
2e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * Copyright 2018 The Android Open Source Project
3e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer *
4e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * Licensed under the Apache License, Version 2.0 (the "License");
5e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * you may not use this file except in compliance with the License.
6e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * You may obtain a copy of the License at
7e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer *
8e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer *      http://www.apache.org/licenses/LICENSE-2.0
9e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer *
10e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * Unless required by applicable law or agreed to in writing, software
11e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * distributed under the License is distributed on an "AS IS" BASIS,
12e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * See the License for the specific language governing permissions and
14e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer * limitations under the License.
15e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer */
16e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
17e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerpackage androidx.webkit;
18e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
19e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.graphics.Bitmap;
20e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.net.Uri;
21e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.support.test.filters.MediumTest;
22e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.support.test.runner.AndroidJUnit4;
23e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.webkit.ValueCallback;
24e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.webkit.WebResourceRequest;
25e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.webkit.WebResourceResponse;
26e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.webkit.WebView;
27e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport android.webkit.WebViewClient;
28e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
29e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport org.junit.Assert;
30e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport org.junit.Assume;
31e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport org.junit.Before;
32e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport org.junit.Test;
33e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport org.junit.runner.RunWith;
34e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
35e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport java.util.HashMap;
36e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport java.util.Map;
37e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport java.util.concurrent.CountDownLatch;
38e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerimport java.util.concurrent.TimeUnit;
39e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
40e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer@MediumTest
41e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer@RunWith(AndroidJUnit4.class)
42e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischerpublic class WebViewClientCompatTest {
43e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    private WebViewOnUiThread mWebViewOnUiThread;
44e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
45e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    private static final long TEST_TIMEOUT = 20000L;
46e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    private static final String TEST_URL = "http://www.example.com/";
47e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    private static final String TEST_SAFE_BROWSING_URL =
48e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            "chrome://safe-browsing/match?type=malware";
49e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
50e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    @Before
51e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    public void setUp() {
52e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread = new WebViewOnUiThread();
53e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
54e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
55e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    @Test
56e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    public void testShouldOverrideUrlLoadingDefault() {
57e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        // This never calls into chromium, so we don't need to do any feature checks.
58e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
59e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final MockWebViewClient webViewClient = new MockWebViewClient();
60e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
61e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        // Create any valid WebResourceRequest, the return values don't matter much.
62e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final WebResourceRequest resourceRequest = new WebResourceRequest() {
63e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            @Override
64e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            public Uri getUrl() {
65e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                return Uri.parse(TEST_URL);
66e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            }
67e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
68e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            @Override
69e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            public boolean isForMainFrame() {
70e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                return false;
71e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            }
72e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
73e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            @Override
74e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            public boolean isRedirect() {
75e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                return false;
76e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            }
77e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
78e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            @Override
79e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            public boolean hasGesture() {
80e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                return false;
81e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            }
82e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
83e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            @Override
84e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            public String getMethod() {
85e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                return "GET";
86e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            }
87e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
88e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            @Override
89e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            public Map<String, String> getRequestHeaders() {
90e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                return new HashMap<String, String>();
91e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            }
92e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        };
93e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
94e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertFalse(webViewClient.shouldOverrideUrlLoading(
95e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                mWebViewOnUiThread.getWebViewOnCurrentThread(), resourceRequest));
96e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
97e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
98e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    @Test
99e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    public void testShouldOverrideUrlLoading() throws InterruptedException {
100e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(
101e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                WebViewFeature.isFeatureSupported(WebViewFeature.SHOULD_OVERRIDE_WITH_REDIRECTS));
102e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(
103e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                WebViewFeature.isFeatureSupported(WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT));
104e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
105e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        String data = "<html><body>"
106e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                + "<a href=\"" + TEST_URL + "\" id=\"link\">new page</a>"
107e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                + "</body></html>";
108e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
109302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer        final CountDownLatch pageFinishedLatch = new CountDownLatch(1);
110302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer        final MockWebViewClient webViewClient = new MockWebViewClient() {
111302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer            @Override
112302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer            public void onPageFinished(WebView view, String url) {
113302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer                super.onPageFinished(view, url);
114302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer                pageFinishedLatch.countDown();
115302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer            }
116302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer        };
117302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer        mWebViewOnUiThread.setWebViewClient(webViewClient);
118302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer        mWebViewOnUiThread.getSettings().setJavaScriptEnabled(true);
119e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        clickOnLinkUsingJs("link", mWebViewOnUiThread);
120302ebcc433c33d90c81a2c4db3993233b348808fNate Fischer        pageFinishedLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
121e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertEquals(TEST_URL,
122e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                webViewClient.getLastShouldOverrideResourceRequest().getUrl().toString());
123e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
124e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        WebResourceRequest request = webViewClient.getLastShouldOverrideResourceRequest();
125e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertNotNull(request);
126e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertTrue(request.isForMainFrame());
127e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertFalse(WebResourceRequestCompat.isRedirect(request));
128e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertFalse(request.hasGesture());
129e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
130e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
131e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    private void clickOnLinkUsingJs(final String linkId, WebViewOnUiThread webViewOnUiThread)
132e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            throws InterruptedException {
133e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final CountDownLatch callbackLatch = new CountDownLatch(1);
134e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        ValueCallback<String> callback = new ValueCallback<String>() {
135e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            @Override
136e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            public void onReceiveValue(String value) {
137e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                callbackLatch.countDown();
138e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            }
139e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        };
140e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        webViewOnUiThread.evaluateJavascript(
141e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                "document.getElementById('" + linkId + "').click();"
142e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                        + "console.log('element with id [" + linkId + "] clicked');", callback);
143e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
144e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
145e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
146e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    @Test
147e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    public void testOnReceivedError() throws Exception {
148e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(
149e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                WebViewFeature.isFeatureSupported(WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR));
150e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(
151e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                WebViewFeature.isFeatureSupported(WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE));
152e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
153e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final MockWebViewClient webViewClient = new MockWebViewClient();
154e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.setWebViewClient(webViewClient);
155e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
156e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        String wrongUri = "invalidscheme://some/resource";
157e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertNull(webViewClient.getOnReceivedResourceError());
158e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.loadUrlAndWaitForCompletion(wrongUri);
159e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertNotNull(webViewClient.getOnReceivedResourceError());
160e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertEquals(WebViewClient.ERROR_UNSUPPORTED_SCHEME,
161e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                webViewClient.getOnReceivedResourceError().getErrorCode());
162e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
163e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
164e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    @Test
165e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    public void testOnReceivedErrorForSubresource() throws Exception {
166e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(
167e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                WebViewFeature.isFeatureSupported(WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR));
168e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
169e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final MockWebViewClient webViewClient = new MockWebViewClient();
170e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.setWebViewClient(webViewClient);
171e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
172e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertNull(webViewClient.getOnReceivedResourceError());
173e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        String data = "<html>"
174e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                + "  <body>"
175e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                + "    <img src=\"invalidscheme://some/resource\" />"
176e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                + "  </body>"
177e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                + "</html>";
178e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
179e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
180e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertNotNull(webViewClient.getOnReceivedResourceError());
181e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertEquals(WebViewClient.ERROR_UNSUPPORTED_SCHEME,
182e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                webViewClient.getOnReceivedResourceError().getErrorCode());
183e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
184e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
185e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    @Test
186d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    public void testOnSafeBrowsingHitBackToSafety() throws Throwable {
187e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_HIT));
188e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_ENABLE));
189d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assume.assumeTrue(WebViewFeature.isFeatureSupported(
190d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer                WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY));
191e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(
192e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                WebViewFeature.isFeatureSupported(WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE));
193e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
194e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final SafeBrowsingBackToSafetyClient backToSafetyWebViewClient =
195e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                new SafeBrowsingBackToSafetyClient();
196e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.setWebViewClient(backToSafetyWebViewClient);
197e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        WebSettingsCompat.setSafeBrowsingEnabled(mWebViewOnUiThread.getSettings(), true);
198e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
199e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        // Load any page
200e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        String data = "<html><body>some safe page</body></html>";
201e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
202e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final String originalUrl = mWebViewOnUiThread.getUrl();
203e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
204d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        enableSafeBrowsingAndLoadUnsafePage(backToSafetyWebViewClient);
205d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer
206d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        // Back to safety should produce a network error
207d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assert.assertNotNull(backToSafetyWebViewClient.getOnReceivedResourceError());
208d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assert.assertEquals(WebViewClient.ERROR_UNSAFE_RESOURCE,
209d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer                backToSafetyWebViewClient.getOnReceivedResourceError().getErrorCode());
210d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer
211d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        // Check that we actually navigated backward
212d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assert.assertEquals(originalUrl, mWebViewOnUiThread.getUrl());
213d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    }
214d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer
215d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    @Test
216d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    public void testOnSafeBrowsingHitProceed() throws Throwable {
217d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assume.assumeTrue(WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_HIT));
218d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assume.assumeTrue(WebViewFeature.isFeatureSupported(WebViewFeature.SAFE_BROWSING_ENABLE));
219d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assume.assumeTrue(WebViewFeature.isFeatureSupported(
220d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer                WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED));
221e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
222e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final SafeBrowsingProceedClient proceedWebViewClient = new SafeBrowsingProceedClient();
223e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.setWebViewClient(proceedWebViewClient);
224d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        WebSettingsCompat.setSafeBrowsingEnabled(mWebViewOnUiThread.getSettings(), true);
225e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
226d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        // Load any page
227d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        String data = "<html><body>some safe page</body></html>";
228d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        mWebViewOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
229e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
230d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        enableSafeBrowsingAndLoadUnsafePage(proceedWebViewClient);
231e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
232d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        // Check that we actually proceeded
233d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assert.assertEquals(TEST_SAFE_BROWSING_URL, mWebViewOnUiThread.getUrl());
234d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    }
235d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer
236d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    private void enableSafeBrowsingAndLoadUnsafePage(SafeBrowsingClient client) throws Throwable {
237d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        // Note: Safe Browsing depends on user opt-in as well, so we can't assume it's actually
238d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        // enabled. #getSafeBrowsingEnabled will tell us the true state of whether Safe Browsing is
239d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        // enabled.
240d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        boolean deviceSupportsSafeBrowsing =
241d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer                WebSettingsCompat.getSafeBrowsingEnabled(mWebViewOnUiThread.getSettings());
242d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assume.assumeTrue(deviceSupportsSafeBrowsing);
243d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer
244d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assert.assertNull(client.getOnReceivedResourceError());
245d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        mWebViewOnUiThread.loadUrlAndWaitForCompletion(TEST_SAFE_BROWSING_URL);
246d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer
247d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assert.assertEquals(TEST_SAFE_BROWSING_URL,
248d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer                client.getOnSafeBrowsingHitRequest().getUrl().toString());
249d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        Assert.assertTrue(client.getOnSafeBrowsingHitRequest().isForMainFrame());
250e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
251e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
252e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    @Test
253e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    public void testOnPageCommitVisibleCalled() throws Exception {
254e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assume.assumeTrue(WebViewFeature.isFeatureSupported(WebViewFeature.VISUAL_STATE_CALLBACK));
255e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
256e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        final CountDownLatch callbackLatch = new CountDownLatch(1);
257e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
258e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.setWebViewClient(new WebViewClientCompat() {
259e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            @Override
260e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            public void onPageCommitVisible(WebView view, String url) {
261e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                Assert.assertEquals(url, "about:blank");
262e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                callbackLatch.countDown();
263e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            }
264e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        });
265e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
266e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        mWebViewOnUiThread.loadUrl("about:blank");
267e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        Assert.assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
268e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
269e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
270e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    private class MockWebViewClient extends WebViewOnUiThread.WaitForLoadedClient {
271e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        private boolean mOnPageStartedCalled;
272e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        private boolean mOnPageFinishedCalled;
273e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        private boolean mOnLoadResourceCalled;
274e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        private WebResourceErrorCompat mOnReceivedResourceError;
275e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        private WebResourceResponse mOnReceivedHttpError;
276e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        private WebResourceRequest mLastShouldOverrideResourceRequest;
277e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
278e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        MockWebViewClient() {
279e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            super(mWebViewOnUiThread);
280e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
281e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
282e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public WebResourceErrorCompat getOnReceivedResourceError() {
283e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            return mOnReceivedResourceError;
284e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
285e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
286e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public WebResourceResponse getOnReceivedHttpError() {
287e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            return mOnReceivedHttpError;
288e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
289e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
290e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public WebResourceRequest getLastShouldOverrideResourceRequest() {
291e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            return mLastShouldOverrideResourceRequest;
292e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
293e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
294e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
295e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public void onPageStarted(WebView view, String url, Bitmap favicon) {
296e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            super.onPageStarted(view, url, favicon);
297e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            mOnPageStartedCalled = true;
298e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
299e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
300e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
301e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public void onPageFinished(WebView view, String url) {
302e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            super.onPageFinished(view, url);
303e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            Assert.assertTrue(mOnPageStartedCalled);
304e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            Assert.assertTrue(mOnLoadResourceCalled);
305e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            mOnPageFinishedCalled = true;
306e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
307e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
308e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
309e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public void onLoadResource(WebView view, String url) {
310e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            super.onLoadResource(view, url);
311e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            Assert.assertTrue(mOnPageStartedCalled);
312e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            mOnLoadResourceCalled = true;
313e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
314e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
315e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
316e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @SuppressWarnings("deprecation")
317e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public void onReceivedError(WebView view, int errorCode,
318e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                String description, String failingUrl) {
319e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            // This can be called if a test runs for a WebView which does not support the {@link
320e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            // WebViewFeature#RECEIVE_WEB_RESOURCE_ERROR} feature.
321e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
322e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
323e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
324e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public void onReceivedError(WebView view, WebResourceRequest request,
325e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                WebResourceErrorCompat error) {
326e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            mOnReceivedResourceError = error;
327e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
328e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
329e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
330e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public void onReceivedHttpError(WebView view, WebResourceRequest request,
331e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                WebResourceResponse errorResponse) {
332e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            super.onReceivedHttpError(view, request, errorResponse);
333e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            mOnReceivedHttpError = errorResponse;
334e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
335e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
336e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
337e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @SuppressWarnings("deprecation")
338e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public boolean shouldOverrideUrlLoading(WebView view, String url) {
339e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            // This can be called if a test runs for a WebView which does not support the {@link
340e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            // WebViewFeature#SHOULD_OVERRIDE_WITH_REDIRECTS} feature.
341e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            return false;
342e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
343e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
344e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
345e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
346e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            mLastShouldOverrideResourceRequest = request;
347e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            return false;
348e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
349e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
350e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
351d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    private class SafeBrowsingClient extends MockWebViewClient {
352e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        private WebResourceRequest mOnSafeBrowsingHitRequest;
353e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        private int mOnSafeBrowsingHitThreatType;
354e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
355e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public WebResourceRequest getOnSafeBrowsingHitRequest() {
356e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            return mOnSafeBrowsingHitRequest;
357e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
358e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
359d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        public void setOnSafeBrowsingHitRequest(WebResourceRequest request) {
360d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer            mOnSafeBrowsingHitRequest = request;
361d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        }
362d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer
363e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public int getOnSafeBrowsingHitThreatType() {
364e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            return mOnSafeBrowsingHitThreatType;
365e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
366e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
367d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        public void setOnSafeBrowsingHitThreatType(int type) {
368d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer            mOnSafeBrowsingHitThreatType = type;
369d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer        }
370d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    }
371d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer
372d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    private class SafeBrowsingBackToSafetyClient extends SafeBrowsingClient {
373e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
374e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
375e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                int threatType, SafeBrowsingResponseCompat response) {
376e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            // Immediately go back to safety to return the network error code
377d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer            setOnSafeBrowsingHitRequest(request);
378d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer            setOnSafeBrowsingHitThreatType(threatType);
379e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            response.backToSafety(/* report */ true);
380e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
381e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
382e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer
383d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer    private class SafeBrowsingProceedClient extends SafeBrowsingClient {
384e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        @Override
385e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
386e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer                int threatType, SafeBrowsingResponseCompat response) {
387e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            // Proceed through Safe Browsing warnings
388d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer            setOnSafeBrowsingHitRequest(request);
389d2cb2ed027d1e90e87f7bd9413f73bafef6ef7c8Nate Fischer            setOnSafeBrowsingHitThreatType(threatType);
390e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer            response.proceed(/* report */ true);
391e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer        }
392e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer    }
393e7734de3cf9236fbba6af94873d9ada4a4a89af1Nate Fischer}
394