1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.android_webview.test;
6 
7 import static org.chromium.android_webview.test.AwActivityTestRule.WAIT_TIMEOUT_MS;
8 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.MULTI_PROCESS;
9 import static org.chromium.android_webview.test.OnlyRunIn.ProcessMode.SINGLE_PROCESS;
10 
11 import android.graphics.Bitmap;
12 import android.graphics.BitmapFactory;
13 import android.graphics.Canvas;
14 import android.graphics.Color;
15 import android.os.Handler;
16 import android.os.Looper;
17 import android.os.Message;
18 import android.support.test.InstrumentationRegistry;
19 import android.util.Pair;
20 import android.view.KeyEvent;
21 import android.view.View;
22 import android.webkit.JavascriptInterface;
23 
24 import androidx.test.filters.LargeTest;
25 import androidx.test.filters.MediumTest;
26 import androidx.test.filters.SmallTest;
27 
28 import com.google.common.collect.ImmutableMap;
29 
30 import org.junit.Assert;
31 import org.junit.Rule;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 
35 import org.chromium.android_webview.AwContents;
36 import org.chromium.android_webview.AwRenderProcess;
37 import org.chromium.android_webview.AwSettings;
38 import org.chromium.android_webview.renderer_priority.RendererPriority;
39 import org.chromium.android_webview.test.TestAwContentsClient.OnDownloadStartHelper;
40 import org.chromium.android_webview.test.util.CommonResources;
41 import org.chromium.base.BuildInfo;
42 import org.chromium.base.Log;
43 import org.chromium.base.metrics.RecordHistogram;
44 import org.chromium.base.test.util.CallbackHelper;
45 import org.chromium.base.test.util.CommandLineFlags;
46 import org.chromium.base.test.util.Feature;
47 import org.chromium.components.viz.common.VizFeatures;
48 import org.chromium.content_public.browser.test.util.TestThreadUtils;
49 import org.chromium.content_public.common.ContentSwitches;
50 import org.chromium.content_public.common.ContentUrlConstants;
51 import org.chromium.net.test.EmbeddedTestServer;
52 import org.chromium.net.test.util.TestWebServer;
53 
54 import java.io.InputStream;
55 import java.net.URL;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.concurrent.Semaphore;
61 import java.util.concurrent.TimeUnit;
62 import java.util.concurrent.TimeoutException;
63 import java.util.concurrent.atomic.AtomicInteger;
64 
65 /**
66  * AwContents tests.
67  */
68 @RunWith(AwJUnit4ClassRunner.class)
69 public class AwContentsTest {
70     private static final String TAG = "AwContentsTest";
71 
72     @Rule
73     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
74 
75     private TestAwContentsClient mContentsClient = new TestAwContentsClient();
76     private volatile Integer mHistogramTotalCount = 0;
77 
78     @Test
79     @SmallTest
80     @Feature({"AndroidWebView"})
testCreateDestroy()81     public void testCreateDestroy() throws Throwable {
82         // NOTE this test runs on UI thread, so we cannot call any async methods.
83         mActivityTestRule.runOnUiThread(() -> mActivityTestRule.createAwTestContainerView(
84                 mContentsClient)
85                 .getAwContents()
86                 .destroy());
87     }
88 
89     @Test
90     @SmallTest
91     @Feature({"AndroidWebView"})
testCreateLoadPageDestroy()92     public void testCreateLoadPageDestroy() throws Throwable {
93         AwTestContainerView awTestContainerView =
94                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
95         mActivityTestRule.loadDataSync(awTestContainerView.getAwContents(),
96                 mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML, "text/html",
97                 false);
98 
99         mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
100         // It should be safe to call destroy multiple times.
101         mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
102     }
103 
104     @Test
105     @LargeTest
106     @Feature({"AndroidWebView"})
testCreateLoadDestroyManyTimes()107     public void testCreateLoadDestroyManyTimes() throws Throwable {
108         for (int i = 0; i < 10; ++i) {
109             AwTestContainerView testView =
110                     mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
111             AwContents awContents = testView.getAwContents();
112 
113             mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
114                     ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
115             mActivityTestRule.destroyAwContentsOnMainSync(awContents);
116         }
117     }
118 
119     @Test
120     @LargeTest
121     @Feature({"AndroidWebView"})
testCreateLoadDestroyManyAtOnce()122     public void testCreateLoadDestroyManyAtOnce() throws Throwable {
123         AwTestContainerView views[] = new AwTestContainerView[10];
124 
125         for (int i = 0; i < views.length; ++i) {
126             views[i] = mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
127             mActivityTestRule.loadUrlSync(views[i].getAwContents(),
128                     mContentsClient.getOnPageFinishedHelper(),
129                     ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
130         }
131 
132         for (int i = 0; i < views.length; ++i) {
133             mActivityTestRule.destroyAwContentsOnMainSync(views[i].getAwContents());
134             views[i] = null;
135         }
136     }
137 
138     @Test
139     @SmallTest
140     @Feature({"AndroidWebView"})
testWebViewApisFailGracefullyAfterDestruction()141     public void testWebViewApisFailGracefullyAfterDestruction() throws Throwable {
142         mActivityTestRule.runOnUiThread(() -> {
143             AwContents awContents = mActivityTestRule.createAwTestContainerView(mContentsClient)
144                     .getAwContents();
145             awContents.destroy();
146 
147             // The documentation for WebView#destroy() reads "This method should be called
148             // after this WebView has been removed from the view system. No other methods
149             // may be called on this WebView after destroy".
150             // However, some apps do not respect that restriction so we need to ensure that
151             // we fail gracefully and do not crash when APIs are invoked after destruction.
152             // Due to the large number of APIs we only test a representative selection here.
153             awContents.clearHistory();
154             Assert.assertNull(awContents.getOriginalUrl());
155             Assert.assertNull(awContents.getNavigationHistory());
156             awContents.loadUrl("http://www.google.com");
157             awContents.findAllAsync("search");
158             Assert.assertNull(awContents.getUrl());
159             Assert.assertFalse(awContents.canGoBack());
160             awContents.disableJavascriptInterfacesInspection();
161             awContents.invokeZoomPicker();
162             awContents.onResume();
163             awContents.stopLoading();
164             awContents.onWindowVisibilityChanged(View.VISIBLE);
165             awContents.requestFocus();
166             awContents.isMultiTouchZoomSupported();
167             awContents.setOverScrollMode(View.OVER_SCROLL_NEVER);
168             awContents.pauseTimers();
169             awContents.onContainerViewScrollChanged(200, 200, 100, 100);
170             awContents.computeScroll();
171             awContents.onMeasure(100, 100);
172             awContents.onDraw(new Canvas());
173             awContents.getMostRecentProgress();
174             Assert.assertEquals(0, awContents.computeHorizontalScrollOffset());
175             Assert.assertEquals(0, awContents.getContentWidthCss());
176             awContents.onKeyUp(KeyEvent.KEYCODE_BACK,
177                     new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
178         });
179     }
180 
181     @Test
182     @SmallTest
183     @Feature({"AndroidWebView"})
testUseAwSettingsAfterDestroy()184     public void testUseAwSettingsAfterDestroy() throws Throwable {
185         AwTestContainerView awTestContainerView =
186                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
187         AwSettings awSettings =
188                 mActivityTestRule.getAwSettingsOnUiThread(awTestContainerView.getAwContents());
189         mActivityTestRule.loadDataSync(awTestContainerView.getAwContents(),
190                 mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML, "text/html",
191                 false);
192         mActivityTestRule.destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
193 
194         // AwSettings should still be usable even after native side is destroyed.
195         String newFontFamily = "serif";
196         awSettings.setStandardFontFamily(newFontFamily);
197         Assert.assertEquals(newFontFamily, awSettings.getStandardFontFamily());
198         boolean newBlockNetworkLoads = !awSettings.getBlockNetworkLoads();
199         awSettings.setBlockNetworkLoads(newBlockNetworkLoads);
200         Assert.assertEquals(newBlockNetworkLoads, awSettings.getBlockNetworkLoads());
201     }
202 
203     @Test
204     @SmallTest
205     @Feature({"AndroidWebView"})
testBackgroundColorInDarkMode()206     public void testBackgroundColorInDarkMode() throws Throwable {
207         mActivityTestRule.runOnUiThread(() -> {
208             AwContents awContents =
209                     mActivityTestRule.createAwTestContainerView(mContentsClient).getAwContents();
210             AwSettings awSettings = awContents.getSettings();
211 
212             Assert.assertEquals(awContents.getEffectiveBackgroundColorForTesting(), Color.WHITE);
213 
214             awSettings.setForceDarkMode(AwSettings.FORCE_DARK_ON);
215             Assert.assertTrue(awSettings.isDarkMode());
216             Assert.assertEquals(awContents.getEffectiveBackgroundColorForTesting(), Color.BLACK);
217 
218             awContents.setBackgroundColor(Color.RED);
219             Assert.assertEquals(awContents.getEffectiveBackgroundColorForTesting(), Color.RED);
220 
221             awContents.destroy();
222             Assert.assertEquals(awContents.getEffectiveBackgroundColorForTesting(), Color.RED);
223         });
224     }
225 
callDocumentHasImagesSync(final AwContents awContents)226     private int callDocumentHasImagesSync(final AwContents awContents)
227             throws Throwable, InterruptedException {
228         // Set up a container to hold the result object and a semaphore to
229         // make the test wait for the result.
230         final AtomicInteger val = new AtomicInteger();
231         final Semaphore s = new Semaphore(0);
232         final Message msg = Message.obtain(new Handler(Looper.getMainLooper()) {
233             @Override
234             public void handleMessage(Message msg) {
235                 val.set(msg.arg1);
236                 s.release();
237             }
238         });
239         InstrumentationRegistry.getInstrumentation().runOnMainSync(
240                 () -> awContents.documentHasImages(msg));
241         Assert.assertTrue(s.tryAcquire(AwActivityTestRule.WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
242         int result = val.get();
243         return result;
244     }
245 
246     @Test
247     @SmallTest
248     @Feature({"AndroidWebView"})
testDocumentHasImages()249     public void testDocumentHasImages() throws Throwable {
250         AwTestContainerView testView =
251                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
252         AwContents awContents = testView.getAwContents();
253 
254         final CallbackHelper loadHelper = mContentsClient.getOnPageFinishedHelper();
255 
256         final String mime = "text/html";
257         final String emptyDoc = "<head/><body/>";
258         final String imageDoc = "<head/><body><img/><img/></body>";
259 
260         // Make sure a document that does not have images returns 0
261         mActivityTestRule.loadDataSync(awContents, loadHelper, emptyDoc, mime, false);
262         int result = callDocumentHasImagesSync(awContents);
263         Assert.assertEquals(0, result);
264 
265         // Make sure a document that does have images returns 1
266         mActivityTestRule.loadDataSync(awContents, loadHelper, imageDoc, mime, false);
267         result = callDocumentHasImagesSync(awContents);
268         Assert.assertEquals(1, result);
269     }
270 
271     @Test
272     @SmallTest
273     @Feature({"AndroidWebView"})
testClearCacheMemoryAndDisk()274     public void testClearCacheMemoryAndDisk() throws Throwable {
275         final AwTestContainerView testContainer =
276                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
277         final AwContents awContents = testContainer.getAwContents();
278 
279         TestWebServer webServer = TestWebServer.start();
280         try {
281             final String pagePath = "/clear_cache_test.html";
282             List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
283             // Set Cache-Control headers to cache this request. One century should be long enough.
284             headers.add(Pair.create("Cache-Control", "max-age=3153600000"));
285             headers.add(Pair.create("Last-Modified", "Wed, 3 Oct 2012 00:00:00 GMT"));
286             final String pageUrl = webServer.setResponse(
287                     pagePath, "<html><body>foo</body></html>", headers);
288 
289             // First load to populate cache.
290             mActivityTestRule.clearCacheOnUiThread(awContents, true);
291             mActivityTestRule.loadUrlSync(
292                     awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
293             Assert.assertEquals(1, webServer.getRequestCount(pagePath));
294 
295             // Load about:blank so next load is not treated as reload by webkit and force
296             // revalidate with the server.
297             mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
298                     ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
299 
300             // No clearCache call, so should be loaded from cache.
301             mActivityTestRule.loadUrlSync(
302                     awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
303             Assert.assertEquals(1, webServer.getRequestCount(pagePath));
304 
305             // Same as above.
306             mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
307                     ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
308 
309             // Clear cache, so should hit server again.
310             mActivityTestRule.clearCacheOnUiThread(awContents, true);
311             mActivityTestRule.loadUrlSync(
312                     awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
313             Assert.assertEquals(2, webServer.getRequestCount(pagePath));
314         } finally {
315             webServer.shutdown();
316         }
317     }
318 
319     @Test
320     @SmallTest
321     @Feature({"AndroidWebView"})
testClearCacheInQuickSuccession()322     public void testClearCacheInQuickSuccession() {
323         final AwTestContainerView testContainer =
324                 mActivityTestRule.createAwTestContainerViewOnMainSync(new TestAwContentsClient());
325         final AwContents awContents = testContainer.getAwContents();
326 
327         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
328             for (int i = 0; i < 10; ++i) {
329                 awContents.clearCache(true);
330             }
331         });
332     }
333 
334     @Test
335     @SmallTest
336     @Feature({"AndroidWebView"})
testGetFavicon()337     public void testGetFavicon() throws Throwable {
338         AwContents.setShouldDownloadFavicons();
339         final AwTestContainerView testView =
340                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
341         final AwContents awContents = testView.getAwContents();
342 
343         TestWebServer webServer = TestWebServer.start();
344         try {
345             final String faviconUrl = webServer.setResponseBase64(
346                     "/" + CommonResources.FAVICON_FILENAME, CommonResources.FAVICON_DATA_BASE64,
347                     CommonResources.getImagePngHeaders(false));
348             final String pageUrl = webServer.setResponse("/favicon.html",
349                     CommonResources.FAVICON_STATIC_HTML, null);
350 
351             // The getFavicon will return the right icon a certain time after
352             // the page load completes which makes it slightly hard to test.
353             final Bitmap defaultFavicon = awContents.getFavicon();
354 
355             mActivityTestRule.getAwSettingsOnUiThread(awContents).setImagesEnabled(true);
356             mActivityTestRule.loadUrlSync(
357                     awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
358 
359             mActivityTestRule.pollUiThread(() -> awContents.getFavicon() != null
360                     && !awContents.getFavicon().sameAs(defaultFavicon));
361 
362             final Object originalFaviconSource = (new URL(faviconUrl)).getContent();
363             final Bitmap originalFavicon =
364                     BitmapFactory.decodeStream((InputStream) originalFaviconSource);
365             Assert.assertNotNull(originalFavicon);
366 
367             Assert.assertTrue(awContents.getFavicon().sameAs(originalFavicon));
368 
369         } finally {
370             webServer.shutdown();
371         }
372     }
373 
374     @Test
375     @Feature({"AndroidWebView", "Downloads"})
376     @SmallTest
testDownload()377     public void testDownload() throws Throwable {
378         downloadAndCheck(null);
379     }
380 
381     @Test
382     @Feature({"AndroidWebView", "Downloads"})
383     @SmallTest
testDownloadWithCustomUserAgent()384     public void testDownloadWithCustomUserAgent() throws Throwable {
385         downloadAndCheck("Custom User Agent");
386     }
387 
downloadAndCheck(String customUserAgent)388     private void downloadAndCheck(String customUserAgent) throws Throwable {
389         AwTestContainerView testView =
390                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
391         AwContents awContents = testView.getAwContents();
392 
393         if (customUserAgent != null) {
394             AwSettings awSettings = mActivityTestRule.getAwSettingsOnUiThread(awContents);
395             awSettings.setUserAgentString(customUserAgent);
396         }
397 
398         final String data = "download data";
399         final String contentDisposition = "attachment;filename=\"download.txt\"";
400         final String mimeType = "text/plain";
401 
402         List<Pair<String, String>> downloadHeaders = new ArrayList<Pair<String, String>>();
403         downloadHeaders.add(Pair.create("Content-Disposition", contentDisposition));
404         downloadHeaders.add(Pair.create("Content-Type", mimeType));
405         downloadHeaders.add(Pair.create("Content-Length", Integer.toString(data.length())));
406 
407         TestWebServer webServer = TestWebServer.start();
408         try {
409             final String pageUrl = webServer.setResponse(
410                     "/download.txt", data, downloadHeaders);
411             final OnDownloadStartHelper downloadStartHelper =
412                     mContentsClient.getOnDownloadStartHelper();
413             final int callCount = downloadStartHelper.getCallCount();
414             mActivityTestRule.loadUrlAsync(awContents, pageUrl);
415             downloadStartHelper.waitForCallback(callCount);
416 
417             Assert.assertEquals(pageUrl, downloadStartHelper.getUrl());
418             Assert.assertEquals(contentDisposition, downloadStartHelper.getContentDisposition());
419             Assert.assertEquals(mimeType, downloadStartHelper.getMimeType());
420             Assert.assertEquals(data.length(), downloadStartHelper.getContentLength());
421             Assert.assertFalse(downloadStartHelper.getUserAgent().isEmpty());
422             if (customUserAgent != null) {
423                 Assert.assertEquals(customUserAgent, downloadStartHelper.getUserAgent());
424             } else {
425                 Assert.assertEquals(
426                         downloadStartHelper.getUserAgent(), AwSettings.getDefaultUserAgent());
427             }
428         } finally {
429             webServer.shutdown();
430         }
431     }
432 
433     @Test
434     @Feature({"AndroidWebView", "setNetworkAvailable"})
435     @SmallTest
testSetNetworkAvailable()436     public void testSetNetworkAvailable() throws Throwable {
437         AwTestContainerView testView =
438                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
439         AwContents awContents = testView.getAwContents();
440         String script = "navigator.onLine";
441 
442         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
443         mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
444                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
445 
446         // Default to "online".
447         Assert.assertEquals("true",
448                 mActivityTestRule.executeJavaScriptAndWaitForResult(
449                         awContents, mContentsClient, script));
450 
451         // Forcing "offline".
452         AwActivityTestRule.setNetworkAvailableOnUiThread(awContents, false);
453         Assert.assertEquals("false",
454                 mActivityTestRule.executeJavaScriptAndWaitForResult(
455                         awContents, mContentsClient, script));
456 
457         // Forcing "online".
458         AwActivityTestRule.setNetworkAvailableOnUiThread(awContents, true);
459         Assert.assertEquals("true",
460                 mActivityTestRule.executeJavaScriptAndWaitForResult(
461                         awContents, mContentsClient, script));
462     }
463 
464 
465     static class JavaScriptObject {
466         private CallbackHelper mCallbackHelper;
JavaScriptObject(CallbackHelper callbackHelper)467         public JavaScriptObject(CallbackHelper callbackHelper) {
468             mCallbackHelper = callbackHelper;
469         }
470 
471         @JavascriptInterface
run()472         public void run() {
473             mCallbackHelper.notifyCalled();
474         }
475     }
476 
477     @Test
478     @Feature({"AndroidWebView", "Android-JavaBridge"})
479     @SmallTest
testJavaBridge()480     public void testJavaBridge() throws Throwable {
481         final AwTestContainerView testView =
482                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
483         final CallbackHelper callback = new CallbackHelper();
484 
485         AwContents awContents = testView.getAwContents();
486         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
487         AwActivityTestRule.addJavascriptInterfaceOnUiThread(
488                 awContents, new JavaScriptObject(callback), "bridge");
489         mActivityTestRule.executeJavaScriptAndWaitForResult(
490                 awContents, mContentsClient, "window.bridge.run();");
491         callback.waitForCallback(0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
492     }
493 
494     @Test
495     @Feature({"AndroidWebView"})
496     @SmallTest
testEscapingOfErrorPage()497     public void testEscapingOfErrorPage() throws Throwable {
498         AwTestContainerView testView =
499                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
500         AwContents awContents = testView.getAwContents();
501         String script = "window.failed == true";
502 
503         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
504         CallbackHelper onPageFinishedHelper = mContentsClient.getOnPageFinishedHelper();
505         int currentCallCount = onPageFinishedHelper.getCallCount();
506         mActivityTestRule.loadUrlAsync(awContents,
507                 "file:///file-that-does-not-exist#<script>window.failed = true;</script>");
508         onPageFinishedHelper.waitForCallback(
509                 currentCallCount, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
510 
511         Assert.assertEquals("false",
512                 mActivityTestRule.executeJavaScriptAndWaitForResult(
513                         awContents, mContentsClient, script));
514     }
515 
516     @Test
517     @Feature({"AndroidWebView"})
518     @SmallTest
testCanInjectHeaders()519     public void testCanInjectHeaders() throws Throwable {
520         final AwTestContainerView testContainer =
521                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
522         final AwContents awContents = testContainer.getAwContents();
523 
524         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
525 
526         EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
527                 InstrumentationRegistry.getInstrumentation().getContext());
528 
529         try {
530             String url = testServer.getURL("/echoheader?X-foo");
531 
532             final Map<String, String> extraHeaders = new HashMap<String, String>();
533             extraHeaders.put("X-foo", "bar");
534             mActivityTestRule.loadUrlSync(
535                     awContents, mContentsClient.getOnPageFinishedHelper(), url, extraHeaders);
536 
537             String xfoo = mActivityTestRule.getJavaScriptResultBodyTextContent(
538                     awContents, mContentsClient);
539             Assert.assertEquals("bar", xfoo);
540 
541             url = testServer.getURL("/echoheader?Referer");
542 
543             mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
544                     url, ImmutableMap.of("Referer", "http://www.example.com/"));
545 
546             String referer = mActivityTestRule.getJavaScriptResultBodyTextContent(
547                     awContents, mContentsClient);
548             Assert.assertEquals("http://www.example.com/", referer);
549         } finally {
550             testServer.stopAndDestroyServer();
551         }
552     }
553 
554     // This is a meta test that we don't accidentally turn off hardware
555     // acceleration in instrumentation tests without notice. Do not add the
556     // @DisableHardwareAccelerationForTest annotation for this test.
557     @Test
558     @Feature({"AndroidWebView"})
559     @SmallTest
testHardwareModeWorks()560     public void testHardwareModeWorks() {
561         AwTestContainerView testContainer =
562                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
563         Assert.assertTrue(testContainer.isHardwareAccelerated());
564         Assert.assertTrue(testContainer.isBackedByHardwareView());
565     }
566 
567     @Test
568     @Feature({"AndroidWebView"})
569     @SmallTest
testBasicCookieFunctionality()570     public void testBasicCookieFunctionality() throws Throwable {
571         AwTestContainerView testView =
572                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
573         AwContents awContents = testView.getAwContents();
574 
575         TestWebServer webServer = TestWebServer.start();
576         try {
577             List<Pair<String, String>> responseHeaders = CommonResources.getTextHtmlHeaders(true);
578             final String cookie = "key=value";
579             responseHeaders.add(Pair.create("Set-Cookie", cookie));
580             final String url = webServer.setResponse("/" + CommonResources.ABOUT_FILENAME,
581                     CommonResources.ABOUT_HTML, responseHeaders);
582             AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
583             mActivityTestRule.loadUrlSync(
584                     awContents, mContentsClient.getOnPageFinishedHelper(), url);
585 
586             final String script = "document.cookie";
587             Assert.assertEquals("\"key=value\"",
588                     mActivityTestRule.executeJavaScriptAndWaitForResult(
589                             awContents, mContentsClient, script));
590         } finally {
591             webServer.shutdown();
592         }
593     }
594 
595     /**
596      * Verifies that Web Notifications and the Push API are not exposed in WebView.
597      */
598     @Test
599     @Feature({"AndroidWebView"})
600     @SmallTest
testPushAndNotificationsDisabled()601     public void testPushAndNotificationsDisabled() throws Throwable {
602         AwTestContainerView testView =
603                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
604         AwContents awContents = testView.getAwContents();
605 
606         String script = "window.Notification || window.PushManager";
607 
608         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
609         mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
610                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
611         Assert.assertEquals("null",
612                 mActivityTestRule.executeJavaScriptAndWaitForResult(
613                         awContents, mContentsClient, script));
614     }
615 
getRendererPriorityOnUiThread(final AwContents awContents)616     private @RendererPriority int getRendererPriorityOnUiThread(final AwContents awContents)
617             throws Exception {
618         return TestThreadUtils.runOnUiThreadBlocking(
619                 () -> awContents.getEffectivePriorityForTesting());
620     }
621 
setRendererPriorityOnUiThread(final AwContents awContents, final @RendererPriority int priority, final boolean waivedWhenNotVisible)622     private void setRendererPriorityOnUiThread(final AwContents awContents,
623             final @RendererPriority int priority, final boolean waivedWhenNotVisible)
624             throws Throwable {
625         mActivityTestRule.runOnUiThread(
626                 () -> awContents.setRendererPriorityPolicy(priority, waivedWhenNotVisible));
627     }
628 
629     @Test
630     @Feature({"AndroidWebView"})
631     @SmallTest
632     @OnlyRunIn(MULTI_PROCESS)
633     @CommandLineFlags.Add(ContentSwitches.RENDER_PROCESS_LIMIT + "=1")
testForegroundPriorityOneProcess()634     public void testForegroundPriorityOneProcess() throws Throwable {
635         final AwTestContainerView view1 =
636                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
637         final AwContents contents1 = view1.getAwContents();
638         final AwTestContainerView view2 =
639                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
640         final AwContents contents2 = view2.getAwContents();
641 
642         mActivityTestRule.loadUrlSync(contents1, mContentsClient.getOnPageFinishedHelper(),
643                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
644         mActivityTestRule.loadUrlSync(contents2, mContentsClient.getOnPageFinishedHelper(),
645                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
646 
647         // Process should start out high.
648         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents1));
649         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2));
650 
651         // Set one to low. Process should take max priority of contents, so still high.
652         setRendererPriorityOnUiThread(contents1, RendererPriority.LOW, false);
653         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents1));
654         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2));
655 
656         // Set both to low and check.
657         setRendererPriorityOnUiThread(contents2, RendererPriority.LOW, false);
658         Assert.assertEquals(RendererPriority.LOW, getRendererPriorityOnUiThread(contents1));
659         Assert.assertEquals(RendererPriority.LOW, getRendererPriorityOnUiThread(contents2));
660 
661         // Set both to waive and check.
662         setRendererPriorityOnUiThread(contents1, RendererPriority.WAIVED, false);
663         setRendererPriorityOnUiThread(contents2, RendererPriority.WAIVED, false);
664         Assert.assertEquals(RendererPriority.WAIVED, getRendererPriorityOnUiThread(contents1));
665         Assert.assertEquals(RendererPriority.WAIVED, getRendererPriorityOnUiThread(contents2));
666 
667         // Set one to high and check.
668         setRendererPriorityOnUiThread(contents1, RendererPriority.HIGH, false);
669         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents1));
670         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2));
671 
672         // Destroy contents with high priority, and process should fall back to low.
673         // Destroy posts on UI, but getRendererPriorityOnUiThread posts after, so there should
674         // be no flakiness and no need for polling.
675         mActivityTestRule.destroyAwContentsOnMainSync(contents1);
676         Assert.assertEquals(RendererPriority.WAIVED, getRendererPriorityOnUiThread(contents2));
677     }
678 
679     @Test
680     @Feature({"AndroidWebView"})
681     @SmallTest
682     @OnlyRunIn(MULTI_PROCESS)
683     @CommandLineFlags.Add(ContentSwitches.RENDER_PROCESS_LIMIT + "=2")
testForegroundPriorityTwoProcesses()684     public void testForegroundPriorityTwoProcesses() throws Throwable {
685         final AwTestContainerView view1 =
686                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
687         final AwContents contents1 = view1.getAwContents();
688         final AwTestContainerView view2 =
689                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
690         final AwContents contents2 = view2.getAwContents();
691 
692         mActivityTestRule.loadUrlSync(contents1, mContentsClient.getOnPageFinishedHelper(),
693                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
694         mActivityTestRule.loadUrlSync(contents2, mContentsClient.getOnPageFinishedHelper(),
695                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
696 
697         // Process should start out high.
698         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents1));
699         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2));
700 
701         // Set one to low. Other should not be affected.
702         setRendererPriorityOnUiThread(contents1, RendererPriority.LOW, false);
703         Assert.assertEquals(RendererPriority.LOW, getRendererPriorityOnUiThread(contents1));
704         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(contents2));
705     }
706 
707     @Test
708     @Feature({"AndroidWebView"})
709     @SmallTest
710     @OnlyRunIn(MULTI_PROCESS)
testBackgroundPriority()711     public void testBackgroundPriority() throws Throwable {
712         final AwContents awContents =
713                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient)
714                         .getAwContents();
715         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(awContents));
716 
717         mActivityTestRule.runOnUiThread(() -> awContents.onPause());
718         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(awContents));
719 
720         setRendererPriorityOnUiThread(
721                 awContents, RendererPriority.HIGH, true /* waivedWhenNotVisible */);
722         Assert.assertEquals(RendererPriority.WAIVED, getRendererPriorityOnUiThread(awContents));
723 
724         mActivityTestRule.runOnUiThread(() -> awContents.onResume());
725         Assert.assertEquals(RendererPriority.HIGH, getRendererPriorityOnUiThread(awContents));
726     }
727 
728     @Test
729     @Feature({"AndroidWebView"})
730     @SmallTest
731     @OnlyRunIn(MULTI_PROCESS)
testPauseDestroyResume()732     public void testPauseDestroyResume() throws Throwable {
733         mActivityTestRule.runOnUiThread(() -> {
734             AwContents awContents;
735             awContents = mActivityTestRule.createAwTestContainerView(mContentsClient)
736                     .getAwContents();
737             awContents.pauseTimers();
738             awContents.pauseTimers();
739             awContents.destroy();
740             awContents = mActivityTestRule.createAwTestContainerView(mContentsClient)
741                     .getAwContents();
742             awContents.resumeTimers();
743         });
744     }
745 
getRenderProcessOnUiThread(final AwContents awContents)746     private AwRenderProcess getRenderProcessOnUiThread(final AwContents awContents)
747             throws Exception {
748         return TestThreadUtils.runOnUiThreadBlocking(() -> awContents.getRenderProcess());
749     }
750 
751     @Test
752     @Feature({"AndroidWebView"})
753     @SmallTest
754     @OnlyRunIn(MULTI_PROCESS)
testRenderProcessInMultiProcessMode()755     public void testRenderProcessInMultiProcessMode() throws Throwable {
756         AwTestContainerView testView =
757                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
758         final AwContents awContents = testView.getAwContents();
759 
760         final AwRenderProcess preLoadRenderProcess = getRenderProcessOnUiThread(awContents);
761         Assert.assertNotNull(preLoadRenderProcess);
762 
763         mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
764                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
765 
766         final AwRenderProcess postLoadRenderProcess = getRenderProcessOnUiThread(awContents);
767         Assert.assertEquals(preLoadRenderProcess, postLoadRenderProcess);
768     }
769 
770     @Test
771     @Feature({"AndroidWebView"})
772     @SmallTest
773     @OnlyRunIn(SINGLE_PROCESS)
testNoRenderProcessInSingleProcessMode()774     public void testNoRenderProcessInSingleProcessMode() throws Throwable {
775         AwTestContainerView testView =
776                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
777         final AwContents awContents = testView.getAwContents();
778 
779         mActivityTestRule.loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
780                 ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL);
781 
782         final AwRenderProcess renderProcess = getRenderProcessOnUiThread(awContents);
783         Assert.assertEquals(renderProcess, null);
784     }
785 
786     /** Regression test for https://crbug.com/732976. Load a data URL, then immediately
787      * after that load a javascript URL. The data URL navigation shouldn't be blocked.
788      */
789     @Test
790     @LargeTest
791     @Feature({"AndroidWebView"})
testJavaScriptUrlAfterLoadData()792     public void testJavaScriptUrlAfterLoadData() throws Throwable {
793         AwTestContainerView testView =
794                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
795         final AwContents awContents = testView.getAwContents();
796         mActivityTestRule.runOnUiThread(() -> {
797             // Run javascript navigation immediately, without waiting for the completion of data
798             // URL.
799             awContents.loadData("<html>test</html>", "text/html", "utf-8");
800             awContents.loadUrl("javascript: void(0)");
801         });
802 
803         mContentsClient.getOnPageFinishedHelper().waitForCallback(
804                 0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
805         Assert.assertEquals("data:text/html,<html>test</html>", awContents.getLastCommittedUrl());
806 
807         TestAwContentsClient.AddMessageToConsoleHelper consoleHelper =
808                 mContentsClient.getAddMessageToConsoleHelper();
809         Assert.assertEquals(0, consoleHelper.getMessages().size());
810     }
811 
812     /**
813      * Regression test for https://crbug.com/1145717. Load a URL that requires fixing and verify
814      * that the legacy behavior is preserved (i.e. that the URL is fixed + that no crashes happen in
815      * the product).
816      *
817      * The main test verification is that there are no crashes.  In particular, this test tries
818      * to verify that the `loadUrl` call above won't trigger:
819      * - NOTREACHED and DwoC in content::NavigationRequest's constructor for about: scheme
820      *   navigations that aren't about:blank nor about:srcdoc
821      * - CHECK in content::NavigationRequest::GetOriginForURLLoaderFactory caused by the
822      *   mismatch between the result of this method and the "about:" process lock.
823      */
824     @Test
825     @LargeTest
826     @Feature({"AndroidWebView"})
testLoadUrlAboutVersion()827     public void testLoadUrlAboutVersion() throws Throwable {
828         AwTestContainerView testView =
829                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
830         final AwContents awContents = testView.getAwContents();
831         mActivityTestRule.runOnUiThread(() -> {
832             // "about:safe-browsing" will be rewritten by
833             // components.url_formatter.UrlFormatter.fixupUrl into
834             // "chrome://safe-browsing/".
835             //
836             // Note that chrome://safe-browsing/ is one of very few chrome://... URLs that work
837             // in Android WebView.  In particular, chrome://version/ wouldn't work.
838             awContents.loadUrl("about:safe-browsing");
839         });
840 
841         mContentsClient.getOnPageFinishedHelper().waitForCallback(
842                 0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
843         Assert.assertEquals("chrome://safe-browsing/", awContents.getLastCommittedUrl());
844     }
845 
doHardwareRenderingSmokeTest()846     private void doHardwareRenderingSmokeTest() throws Throwable {
847         AwTestContainerView testView =
848                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
849         final AwContents awContents = testView.getAwContents();
850         String html = "<html>"
851                 + "  <body style=\""
852                 + "       padding: 0;"
853                 + "       margin: 0;"
854                 + "       display: grid;"
855                 + "       display: grid;"
856                 + "       grid-template-columns: 50% 50%;"
857                 + "       grid-template-rows: 50% 50%;\">"
858                 + "   <div style=\"background-color: rgb(255, 0, 0);\"></div>"
859                 + "   <div style=\"background-color: rgb(0, 255, 0);\"></div>"
860                 + "   <div style=\"background-color: rgb(0, 0, 255);\"></div>"
861                 + "   <div style=\"background-color: rgb(128, 128, 128);\"></div>"
862                 + "  </body>"
863                 + "</html>";
864         mActivityTestRule.loadDataSync(testView.getAwContents(),
865                 mContentsClient.getOnPageFinishedHelper(), html, "text/html", false);
866         mActivityTestRule.waitForVisualStateCallback(testView.getAwContents());
867 
868         int[] lastQuadrantColors = null;
869         // Poll for 10s in case raster is slow.
870         for (int i = 0; i < 100; ++i) {
871             final CallbackHelper callbackHelper = new CallbackHelper();
872             final Object[] resultHolder = new Object[1];
873             mActivityTestRule.runOnUiThread(() -> {
874                 testView.readbackQuadrantColors((int[] result) -> {
875                     resultHolder[0] = result;
876                     callbackHelper.notifyCalled();
877                 });
878             });
879             try {
880                 callbackHelper.waitForFirst();
881             } catch (TimeoutException e) {
882                 Log.w(TAG, "Timeout", e);
883                 continue;
884             }
885             int[] quadrantColors = (int[]) resultHolder[0];
886             lastQuadrantColors = quadrantColors;
887             if (quadrantColors != null && Color.rgb(255, 0, 0) == quadrantColors[0]
888                     && Color.rgb(0, 255, 0) == quadrantColors[1]
889                     && Color.rgb(0, 0, 255) == quadrantColors[2]
890                     && Color.rgb(128, 128, 128) == quadrantColors[3]) {
891                 return;
892             }
893             Thread.sleep(100);
894         }
895         Assert.assertNotNull(lastQuadrantColors);
896         // If this test is failing for your CL, then chances are your change is breaking Android
897         // WebView hardware rendering. Please build the "real" webview and check if this is the
898         // case and if so, fix your CL.
899         Assert.assertEquals(Color.rgb(255, 0, 0), lastQuadrantColors[0]);
900         Assert.assertEquals(Color.rgb(0, 255, 0), lastQuadrantColors[1]);
901         Assert.assertEquals(Color.rgb(0, 0, 255), lastQuadrantColors[2]);
902         Assert.assertEquals(Color.rgb(128, 128, 128), lastQuadrantColors[3]);
903     }
904 
905     @Test
906     @Feature({"AndroidWebView"})
907     @MediumTest
testHardwareRenderingSmokeTest()908     public void testHardwareRenderingSmokeTest() throws Throwable {
909         doHardwareRenderingSmokeTest();
910     }
911 
912     @Test
913     @Feature({"AndroidWebView"})
914     @MediumTest
915     @CommandLineFlags.
916     Add({"enable-features=" + VizFeatures.USE_SKIA_RENDERER, "disable-oop-rasterization"})
testHardwareRenderingSmokeTestSkiaRenderer()917     public void testHardwareRenderingSmokeTestSkiaRenderer() throws Throwable {
918         doHardwareRenderingSmokeTest();
919     }
920 
921     @Test
922     @Feature({"AndroidWebView"})
923     @SmallTest
testFixupOctothorpesInLoadDataContent()924     public void testFixupOctothorpesInLoadDataContent() {
925         // If there are no octothorpes the function should have no effect.
926         final String noOctothorpeString = "<div id='foo1'>This content has no octothorpe</div>";
927         Assert.assertEquals(noOctothorpeString,
928                 AwContents.fixupOctothorpesInLoadDataContent(noOctothorpeString));
929 
930         // One '#' followed by a valid DOM id requires us to duplicate it into a real fragment.
931         Assert.assertEquals("abc%23A#A", AwContents.fixupOctothorpesInLoadDataContent("abc#A"));
932         Assert.assertEquals("abc%23a#a", AwContents.fixupOctothorpesInLoadDataContent("abc#a"));
933         Assert.assertEquals("abc%23Aa#Aa", AwContents.fixupOctothorpesInLoadDataContent("abc#Aa"));
934         Assert.assertEquals("abc%23aA#aA", AwContents.fixupOctothorpesInLoadDataContent("abc#aA"));
935         Assert.assertEquals(
936                 "abc%23a1-_:.#a1-_:.", AwContents.fixupOctothorpesInLoadDataContent("abc#a1-_:."));
937 
938         // One '#' followed by an invalid DOM id just means we encode the '#'.
939         Assert.assertEquals("abc%231", AwContents.fixupOctothorpesInLoadDataContent("abc#1"));
940         Assert.assertEquals("abc%231a", AwContents.fixupOctothorpesInLoadDataContent("abc#1a"));
941         Assert.assertEquals(
942                 "abc%23not valid", AwContents.fixupOctothorpesInLoadDataContent("abc#not valid"));
943         Assert.assertEquals("abc%23a@", AwContents.fixupOctothorpesInLoadDataContent("abc#a@"));
944 
945         // Multiple '#', whether or not they have a valid DOM id afterwards, just means we encode
946         // the '#'.
947         Assert.assertEquals("abc%23%23a", AwContents.fixupOctothorpesInLoadDataContent("abc##a"));
948         Assert.assertEquals("abc%23a%23b", AwContents.fixupOctothorpesInLoadDataContent("abc#a#b"));
949     }
950 
951     @Test
952     @Feature({"AndroidWebView"})
953     @SmallTest
testLoadDataOctothorpeHandling()954     public void testLoadDataOctothorpeHandling() throws Throwable {
955         AwTestContainerView testView =
956                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
957         final AwContents awContents = testView.getAwContents();
958 
959         // Before Android Q, the loadData API is expected to handle the encoding for users.
960         boolean encodeOctothorpes = !BuildInfo.targetsAtLeastQ();
961 
962         // A URL with no '#' character.
963         mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
964                 "<html>test</html>", "text/html", false);
965         Assert.assertEquals("data:text/html,<html>test</html>", awContents.getLastCommittedUrl());
966 
967         // A URL with one '#' character.
968         mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
969                 "<html>test#foo</html>", "text/html", false);
970         String expectedUrl = encodeOctothorpes ? "data:text/html,<html>test%23foo</html>"
971                                                : "data:text/html,<html>test#foo</html>";
972         Assert.assertEquals(expectedUrl, awContents.getLastCommittedUrl());
973 
974         // A URL with many '#' characters.
975         mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
976                 "<html>test#foo#bar#</html>", "text/html", false);
977         expectedUrl = encodeOctothorpes ? "data:text/html,<html>test%23foo%23bar%23</html>"
978                                         : "data:text/html,<html>test#foo#bar#</html>";
979         Assert.assertEquals(expectedUrl, awContents.getLastCommittedUrl());
980 
981         // An already encoded '#' character.
982         mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
983                 "<html>test%23foo</html>", "text/html", false);
984         Assert.assertEquals(
985                 "data:text/html,<html>test%23foo</html>", awContents.getLastCommittedUrl());
986 
987         // A URL with a valid fragment. Before Q, this must be manipulated so that it renders the
988         // same and still scrolls to the fragment location.
989         if (encodeOctothorpes) {
990             String contents = "<div style='height: 5000px'></div><a id='target'>Target</a>#target";
991             mActivityTestRule.loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(),
992                     contents, "text/html", false);
993             Assert.assertEquals(
994                     "data:text/html,<div style='height: 5000px'></div><a id='target'>Target</a>"
995                             + "%23target#target",
996                     awContents.getLastCommittedUrl());
997             // TODO(smcgruer): I can physically see that this has scrolled on the test page, and
998             // have traced scrolling through PaintLayerScrollableArea, but I don't know how to check
999             // it.
1000         }
1001     }
1002 
getHistogramSampleCount(String name)1003     private int getHistogramSampleCount(String name) {
1004         TestThreadUtils.runOnUiThreadBlocking(() -> {
1005             mHistogramTotalCount = RecordHistogram.getHistogramTotalCountForTesting(name);
1006         });
1007         return mHistogramTotalCount;
1008     }
1009 
1010     @Test
1011     @Feature({"AndroidWebView"})
1012     @SmallTest
testLoadUrlRecordsScheme_http()1013     public void testLoadUrlRecordsScheme_http() {
1014         // No need to spin up a web server, since we don't care if the load ever succeeds.
1015         final String httpUrlWithNoRealPage = "http://some.origin/some/path.html";
1016         loadUrlAndCheckScheme(httpUrlWithNoRealPage, AwContents.UrlScheme.HTTP_SCHEME);
1017     }
1018 
1019     @Test
1020     @Feature({"AndroidWebView"})
1021     @SmallTest
testLoadUrlRecordsScheme_javascript()1022     public void testLoadUrlRecordsScheme_javascript() {
1023         loadUrlAndCheckScheme(
1024                 "javascript:console.log('message')", AwContents.UrlScheme.JAVASCRIPT_SCHEME);
1025     }
1026 
1027     @Test
1028     @Feature({"AndroidWebView"})
1029     @SmallTest
testLoadUrlRecordsScheme_fileAndroidAsset()1030     public void testLoadUrlRecordsScheme_fileAndroidAsset() {
1031         loadUrlAndCheckScheme("file:///android_asset/some/asset/page.html",
1032                 AwContents.UrlScheme.FILE_ANDROID_ASSET_SCHEME);
1033     }
1034 
1035     @Test
1036     @Feature({"AndroidWebView"})
1037     @SmallTest
testLoadUrlRecordsScheme_fileRegular()1038     public void testLoadUrlRecordsScheme_fileRegular() {
1039         loadUrlAndCheckScheme("file:///some/path/on/disk.html", AwContents.UrlScheme.FILE_SCHEME);
1040     }
1041 
1042     @Test
1043     @Feature({"AndroidWebView"})
1044     @SmallTest
testLoadUrlRecordsScheme_data()1045     public void testLoadUrlRecordsScheme_data() {
1046         loadUrlAndCheckScheme(
1047                 "data:text/html,<html><body>foo</body></html>", AwContents.UrlScheme.DATA_SCHEME);
1048     }
1049 
1050     @Test
1051     @Feature({"AndroidWebView"})
1052     @SmallTest
testLoadUrlRecordsScheme_blank()1053     public void testLoadUrlRecordsScheme_blank() {
1054         loadUrlAndCheckScheme("about:blank", AwContents.UrlScheme.EMPTY);
1055     }
1056 
loadUrlAndCheckScheme(String url, @AwContents.UrlScheme int expectedSchemeEnum)1057     private void loadUrlAndCheckScheme(String url, @AwContents.UrlScheme int expectedSchemeEnum) {
1058         AwTestContainerView testView =
1059                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
1060         final AwContents awContents = testView.getAwContents();
1061 
1062         Assert.assertEquals(0,
1063                 RecordHistogram.getHistogramTotalCountForTesting(
1064                         AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME));
1065         // Note: we use async because not all loads emit onPageFinished. This relies on the UMA
1066         // metric being logged in the synchronous part of loadUrl().
1067         mActivityTestRule.loadUrlAsync(awContents, url);
1068         Assert.assertEquals(1,
1069                 RecordHistogram.getHistogramTotalCountForTesting(
1070                         AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME));
1071         Assert.assertEquals(1,
1072                 RecordHistogram.getHistogramValueCountForTesting(
1073                         AwContents.LOAD_URL_SCHEME_HISTOGRAM_NAME, expectedSchemeEnum));
1074     }
1075 
1076     @Test
1077     @Feature({"AndroidWebView"})
1078     @SmallTest
testFindAllAsyncEmptySearchString()1079     public void testFindAllAsyncEmptySearchString() {
1080         AwTestContainerView testView =
1081                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
1082         final AwContents awContents = testView.getAwContents();
1083         try {
1084             awContents.findAllAsync(null);
1085             Assert.fail("A null searchString should cause an exception to be thrown");
1086         } catch (IllegalArgumentException e) {
1087             // expected
1088         }
1089     }
1090 
1091     @Test
1092     @Feature({"AndroidWebView"})
1093     @SmallTest
testInsertNullVisualStateCallback()1094     public void testInsertNullVisualStateCallback() {
1095         AwTestContainerView testView =
1096                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
1097         final AwContents awContents = testView.getAwContents();
1098         try {
1099             awContents.insertVisualStateCallback(0, null);
1100             Assert.fail("A null VisualStateCallback should cause an exception to be thrown");
1101         } catch (IllegalArgumentException e) {
1102             // expected
1103         }
1104     }
1105 
1106     private static final String HELLO_WORLD_URL = "/android_webview/test/data/hello_world.html";
1107     private static final String HELLO_WORLD_TITLE = "Hello, World!";
1108     private static final String WEBUI_URL = "chrome://safe-browsing";
1109     private static final String WEBUI_TITLE = "Safe Browsing";
1110 
1111     // Check that we can navigate between a regular web page and a WebUI page
1112     // that's available on AW (chrome://safe-browsing), and that the WebUI page
1113     // loads in its own locked renderer process when in multi-process mode.
1114     @Test
1115     @Feature({"AndroidWebView"})
1116     @SmallTest
1117     @OnlyRunIn(MULTI_PROCESS)
testWebUIUsesDedicatedProcessInMultiProcessMode()1118     public void testWebUIUsesDedicatedProcessInMultiProcessMode() throws Throwable {
1119         AwTestContainerView testView =
1120                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
1121         final AwContents awContents = testView.getAwContents();
1122 
1123         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
1124 
1125         EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
1126                 InstrumentationRegistry.getInstrumentation().getContext());
1127         try {
1128             final String pageUrl = testServer.getURL(HELLO_WORLD_URL);
1129 
1130             mActivityTestRule.loadUrlSync(
1131                     awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
1132             Assert.assertEquals(
1133                     HELLO_WORLD_TITLE, mActivityTestRule.getTitleOnUiThread(awContents));
1134 
1135             final AwRenderProcess rendererProcess1 = getRenderProcessOnUiThread(awContents);
1136             Assert.assertNotNull(rendererProcess1);
1137 
1138             // Until AW gets site isolation, ordinary web content should not be
1139             // locked to origin.
1140             boolean isLocked = TestThreadUtils.runOnUiThreadBlocking(
1141                     () -> rendererProcess1.isProcessLockedToSiteForTesting());
1142             Assert.assertFalse("Initial renderer process should not be locked", isLocked);
1143 
1144             mActivityTestRule.loadUrlSync(
1145                     awContents, mContentsClient.getOnPageFinishedHelper(), WEBUI_URL);
1146             Assert.assertEquals(WEBUI_TITLE, mActivityTestRule.getTitleOnUiThread(awContents));
1147 
1148             final AwRenderProcess webuiProcess = getRenderProcessOnUiThread(awContents);
1149             Assert.assertNotEquals(rendererProcess1, webuiProcess);
1150             // WebUI pages should be locked to origin even on AW.
1151             isLocked = TestThreadUtils.runOnUiThreadBlocking(
1152                     () -> webuiProcess.isProcessLockedToSiteForTesting());
1153             Assert.assertTrue("WebUI process should be locked", isLocked);
1154 
1155             mActivityTestRule.loadUrlSync(
1156                     awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
1157 
1158             final AwRenderProcess rendererProcess2 = getRenderProcessOnUiThread(awContents);
1159             Assert.assertEquals(
1160                     HELLO_WORLD_TITLE, mActivityTestRule.getTitleOnUiThread(awContents));
1161             Assert.assertNotEquals(rendererProcess2, webuiProcess);
1162             isLocked = TestThreadUtils.runOnUiThreadBlocking(
1163                     () -> rendererProcess2.isProcessLockedToSiteForTesting());
1164             Assert.assertFalse("Final renderer process should not be locked", isLocked);
1165         } finally {
1166             testServer.stopAndDestroyServer();
1167         }
1168     }
1169 
1170     // In single-process mode, navigations to WebUI should work, but WebUI does
1171     // not gets process-isolated.
1172     @Test
1173     @Feature({"AndroidWebView"})
1174     @SmallTest
1175     @OnlyRunIn(SINGLE_PROCESS)
testWebUILoadsWithoutProcessIsolationInSingleProcessMode()1176     public void testWebUILoadsWithoutProcessIsolationInSingleProcessMode() throws Throwable {
1177         AwTestContainerView testView =
1178                 mActivityTestRule.createAwTestContainerViewOnMainSync(mContentsClient);
1179         final AwContents awContents = testView.getAwContents();
1180 
1181         AwActivityTestRule.enableJavaScriptOnUiThread(awContents);
1182 
1183         EmbeddedTestServer testServer = EmbeddedTestServer.createAndStartServer(
1184                 InstrumentationRegistry.getInstrumentation().getContext());
1185         try {
1186             final String pageUrl = testServer.getURL(HELLO_WORLD_URL);
1187 
1188             mActivityTestRule.loadUrlSync(
1189                     awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
1190             Assert.assertEquals(
1191                     HELLO_WORLD_TITLE, mActivityTestRule.getTitleOnUiThread(awContents));
1192 
1193             final AwRenderProcess rendererProcess1 = getRenderProcessOnUiThread(awContents);
1194             Assert.assertNull(rendererProcess1);
1195 
1196             mActivityTestRule.loadUrlSync(
1197                     awContents, mContentsClient.getOnPageFinishedHelper(), WEBUI_URL);
1198             Assert.assertEquals(WEBUI_TITLE, mActivityTestRule.getTitleOnUiThread(awContents));
1199 
1200             final AwRenderProcess webuiProcess = getRenderProcessOnUiThread(awContents);
1201             Assert.assertNull(webuiProcess);
1202         } finally {
1203             testServer.stopAndDestroyServer();
1204         }
1205     }
1206 }
1207