Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.webkit.cts;
     17 
     18 import static org.hamcrest.MatcherAssert.assertThat;
     19 import static org.hamcrest.Matchers.greaterThan;
     20 import static org.hamcrest.Matchers.lessThan;
     21 
     22 import android.content.Context;
     23 import android.graphics.Bitmap;
     24 import android.graphics.Color;
     25 import android.net.http.SslError;
     26 import android.os.Build;
     27 import android.os.Message;
     28 import android.platform.test.annotations.AppModeFull;
     29 import android.test.ActivityInstrumentationTestCase2;
     30 import android.util.Base64;
     31 import android.util.Log;
     32 import android.view.ViewGroup;
     33 import android.webkit.ConsoleMessage;
     34 import android.webkit.SslErrorHandler;
     35 import android.webkit.WebChromeClient;
     36 import android.webkit.WebIconDatabase;
     37 import android.webkit.WebResourceResponse;
     38 import android.webkit.WebResourceRequest;
     39 import android.webkit.WebSettings;
     40 import android.webkit.WebSettings.TextSize;
     41 import android.webkit.WebStorage;
     42 import android.webkit.WebView;
     43 import android.webkit.WebViewClient;
     44 import android.webkit.cts.WebViewSyncLoader.WaitForLoadedClient;
     45 import android.webkit.cts.WebViewSyncLoader.WaitForProgressClient;
     46 
     47 import com.android.compatibility.common.util.NullWebViewUtils;
     48 import com.android.compatibility.common.util.PollingCheck;
     49 import com.google.common.util.concurrent.SettableFuture;
     50 
     51 import java.io.ByteArrayInputStream;
     52 import java.io.FileOutputStream;
     53 import java.nio.charset.StandardCharsets;
     54 import java.util.HashMap;
     55 import java.util.Locale;
     56 import java.util.Map;
     57 import java.util.concurrent.CountDownLatch;
     58 import java.util.regex.Matcher;
     59 import java.util.regex.Pattern;
     60 
     61 import java.util.concurrent.BlockingQueue;
     62 import java.util.concurrent.LinkedBlockingQueue;
     63 
     64 /**
     65  * Tests for {@link android.webkit.WebSettings}
     66  */
     67 @AppModeFull
     68 public class WebSettingsTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> {
     69 
     70     private static final int WEBVIEW_TIMEOUT = 5000;
     71     private static final String LOG_TAG = "WebSettingsTest";
     72 
     73     private final String EMPTY_IMAGE_HEIGHT = "0";
     74     private final String NETWORK_IMAGE_HEIGHT = "48";  // See getNetworkImageHtml()
     75     private final String DATA_URL_IMAGE_HTML = "<html>" +
     76             "<head><script>function updateTitle(){" +
     77             "document.title=document.getElementById('img').naturalHeight;}</script></head>" +
     78             "<body onload='updateTitle()'>" +
     79             "<img id='img' onload='updateTitle()' src='data:image/png;base64,iVBORw0KGgoAAA" +
     80             "ANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAAAXNSR0IArs4c6QAAAA1JREFUCB0BAgD9/wAAAAIAAc3j" +
     81             "0SsAAAAASUVORK5CYII=" +
     82             "'></body></html>";
     83     private final String DATA_URL_IMAGE_HEIGHT = "1";
     84 
     85     private WebSettings mSettings;
     86     private CtsTestServer mWebServer;
     87     private WebViewOnUiThread mOnUiThread;
     88     private Context mContext;
     89 
     90     public WebSettingsTest() {
     91         super("android.webkit.cts", WebViewCtsActivity.class);
     92     }
     93 
     94     @Override
     95     protected void setUp() throws Exception {
     96         super.setUp();
     97         WebView webview = getActivity().getWebView();
     98         if (webview != null) {
     99             mOnUiThread = new WebViewOnUiThread(webview);
    100             mSettings = mOnUiThread.getSettings();
    101         }
    102         mContext = getInstrumentation().getTargetContext();
    103     }
    104 
    105     @Override
    106     protected void tearDown() throws Exception {
    107         if (mWebServer != null) {
    108             mWebServer.shutdown();
    109         }
    110         if (mOnUiThread != null) {
    111             mOnUiThread.cleanUp();
    112         }
    113         super.tearDown();
    114     }
    115 
    116     /**
    117      * Verifies that the default user agent string follows the format defined in Android
    118      * compatibility definition (tokens in angle brackets are variables, tokens in square
    119      * brackets are optional):
    120      * <p/>
    121      * Mozilla/5.0 (Linux; Android <version>; [<devicemodel>] [Build/<buildID>]; wv)
    122      * AppleWebKit/<major>.<minor> (KHTML, like Gecko) Version/<major>.<minor>
    123      * Chrome/<major>.<minor>.<branch>.<build>[ Mobile] Safari/<major>.<minor>
    124      */
    125     public void testUserAgentString_default() {
    126         if (!NullWebViewUtils.isWebViewAvailable()) {
    127             return;
    128         }
    129         checkUserAgentStringHelper(mSettings.getUserAgentString(), true);
    130     }
    131 
    132     /**
    133      * Verifies that the useragent testing regex is actually correct, because it's very complex.
    134      */
    135     public void testUserAgentStringTest() {
    136         // All test UAs share the same prefix and suffix; only the middle part varies.
    137         final String prefix = "Mozilla/5.0 (Linux; Android " + Build.VERSION.RELEASE + "; ";
    138         final String suffix = "wv) AppleWebKit/0.0 (KHTML, like Gecko) Version/4.0 Chrome/0.0.0.0 Safari/0.0";
    139 
    140         // Valid cases:
    141         // Both model and build present
    142         checkUserAgentStringHelper(prefix + Build.MODEL + " Build/" + Build.ID + "; " + suffix, true);
    143         // Just model
    144         checkUserAgentStringHelper(prefix + Build.MODEL + "; " + suffix, true);
    145         // Just build
    146         checkUserAgentStringHelper(prefix + "Build/" + Build.ID + "; " + suffix, true);
    147         // Neither
    148         checkUserAgentStringHelper(prefix + suffix, true);
    149 
    150         // Invalid cases:
    151         // No space between model and build
    152         checkUserAgentStringHelper(prefix + Build.MODEL + "Build/" + Build.ID + "; " + suffix, false);
    153         // No semicolon after model and/or build
    154         checkUserAgentStringHelper(prefix + Build.MODEL + " Build/" + Build.ID + suffix, false);
    155         checkUserAgentStringHelper(prefix + Build.MODEL + suffix, false);
    156         checkUserAgentStringHelper(prefix + "Build/" + Build.ID + suffix, false);
    157         // Double semicolon when both omitted
    158         checkUserAgentStringHelper(prefix + "; " + suffix, false);
    159     }
    160 
    161     /**
    162      * Helper function to validate that a given useragent string is or is not valid.
    163      */
    164     private void checkUserAgentStringHelper(final String useragent, boolean shouldMatch) {
    165         String expectedRelease;
    166         if ("REL".equals(Build.VERSION.CODENAME)) {
    167             expectedRelease = Pattern.quote(Build.VERSION.RELEASE);
    168         } else {
    169             // Non-release builds don't include real release version, be lenient.
    170             expectedRelease = "[^;]+";
    171         }
    172 
    173         // Build expected regex inserting the appropriate variables, as this is easier to
    174         // understand and get right than matching any possible useragent and comparing the
    175         // variables afterward.
    176         final String patternString =
    177                 // Release version always has a semicolon after it:
    178                 Pattern.quote("Mozilla/5.0 (Linux; Android ") + expectedRelease + ";" +
    179                 // Model is optional, but if present must have a space first:
    180                 "( " + Pattern.quote(Build.MODEL) + ")?" +
    181                 // Build is optional, but if present must have a space first:
    182                 "( Build/" + Pattern.quote(Build.ID) + ")?" +
    183                 // We want a semicolon before the wv token, but we don't want to have two in a row
    184                 // if both model and build are omitted. Lookbehind assertions ensure either:
    185                 // - the previous character is a semicolon
    186                 // - or the previous character is NOT a semicolon AND a semicolon is added here.
    187                 "((?<=;)|(?<!;);)" +
    188                 // After that we can just check for " wv)" to finish the platform section:
    189                 Pattern.quote(" wv) ") +
    190                 // The rest of the expression is browser tokens and is fairly simple:
    191                 "AppleWebKit/\\d+\\.\\d+ " +
    192                 Pattern.quote("(KHTML, like Gecko) Version/4.0 ") +
    193                 "Chrome/\\d+\\.\\d+\\.\\d+\\.\\d+ " +
    194                 "(Mobile )?Safari/\\d+\\.\\d+";
    195         final Pattern userAgentExpr = Pattern.compile(patternString);
    196         Matcher patternMatcher = userAgentExpr.matcher(useragent);
    197         if (shouldMatch) {
    198             assertTrue(String.format("CDD(3.4.1/C-1-3) User agent string did not match expected pattern. \n" +
    199                             "Expected pattern:\n%s\nActual:\n%s", patternString, useragent),
    200                     patternMatcher.find());
    201         } else {
    202             assertFalse(String.format("Known-bad user agent string incorrectly matched. \n" +
    203                             "Expected pattern:\n%s\nActual:\n%s", patternString, useragent),
    204                     patternMatcher.find());
    205         }
    206     }
    207 
    208     public void testAccessUserAgentString() throws Exception {
    209         if (!NullWebViewUtils.isWebViewAvailable()) {
    210             return;
    211         }
    212         startWebServer();
    213         String url = mWebServer.getUserAgentUrl();
    214 
    215         String defaultUserAgent = mSettings.getUserAgentString();
    216         assertNotNull(defaultUserAgent);
    217         mOnUiThread.loadUrlAndWaitForCompletion(url);
    218         assertEquals(defaultUserAgent, mOnUiThread.getTitle());
    219 
    220         // attempting to set a null string has no effect
    221         mSettings.setUserAgentString(null);
    222         assertEquals(defaultUserAgent, mSettings.getUserAgentString());
    223         mOnUiThread.loadUrlAndWaitForCompletion(url);
    224         assertEquals(defaultUserAgent, mOnUiThread.getTitle());
    225 
    226         // attempting to set an empty string has no effect
    227         mSettings.setUserAgentString("");
    228         assertEquals(defaultUserAgent, mSettings.getUserAgentString());
    229         mOnUiThread.loadUrlAndWaitForCompletion(url);
    230         assertEquals(defaultUserAgent, mOnUiThread.getTitle());
    231 
    232         String customUserAgent = "Cts/test";
    233         mSettings.setUserAgentString(customUserAgent);
    234         assertEquals(customUserAgent, mSettings.getUserAgentString());
    235         mOnUiThread.loadUrlAndWaitForCompletion(url);
    236         assertEquals(customUserAgent, mOnUiThread.getTitle());
    237     }
    238 
    239     public void testAccessAllowFileAccess() {
    240         if (!NullWebViewUtils.isWebViewAvailable()) {
    241             return;
    242         }
    243         // This test is not compatible with 4.0.3
    244         if ("4.0.3".equals(Build.VERSION.RELEASE)) {
    245             return;
    246         }
    247 
    248         assertTrue(mSettings.getAllowFileAccess());
    249 
    250         String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.HELLO_WORLD_URL);
    251         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
    252         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
    253 
    254         fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.BR_TAG_URL);
    255         mSettings.setAllowFileAccess(false);
    256         assertFalse(mSettings.getAllowFileAccess());
    257         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
    258         // android_asset URLs should still be loaded when even with file access
    259         // disabled.
    260         assertEquals(TestHtmlConstants.BR_TAG_TITLE, mOnUiThread.getTitle());
    261 
    262         // Files on the file system should not be loaded.
    263         mOnUiThread.loadUrlAndWaitForCompletion(TestHtmlConstants.LOCAL_FILESYSTEM_URL);
    264         assertEquals(TestHtmlConstants.WEBPAGE_NOT_AVAILABLE_TITLE, mOnUiThread.getTitle());
    265     }
    266 
    267     public void testAccessCacheMode_defaultValue() throws Throwable {
    268         if (!NullWebViewUtils.isWebViewAvailable()) {
    269             return;
    270         }
    271         assertEquals(WebSettings.LOAD_DEFAULT, mSettings.getCacheMode());
    272     }
    273 
    274     private void openIconDatabase() throws InterruptedException {
    275         WebkitUtils.onMainThreadSync(() -> {
    276             // getInstance must run on the UI thread
    277             WebIconDatabase iconDb = WebIconDatabase.getInstance();
    278             String dbPath = getActivity().getFilesDir().toString() + "/icons";
    279             iconDb.open(dbPath);
    280         });
    281         getInstrumentation().waitForIdleSync();
    282         Thread.sleep(100); // Wait for open to be received on the icon db thread.
    283     }
    284 
    285     public void testAccessCacheMode_cacheElseNetwork() throws Throwable {
    286         if (!NullWebViewUtils.isWebViewAvailable()) {
    287             return;
    288         }
    289         openIconDatabase();
    290         final IconListenerClient iconListener = new IconListenerClient();
    291         mOnUiThread.setWebChromeClient(iconListener);
    292         startWebServer();
    293 
    294         mSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
    295         assertEquals(WebSettings.LOAD_CACHE_ELSE_NETWORK, mSettings.getCacheMode());
    296         int initialRequestCount = mWebServer.getRequestCount();
    297         loadAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    298         iconListener.waitForNextIcon();
    299         int requestCountAfterFirstLoad = mWebServer.getRequestCount();
    300         assertTrue("Must fetch non-cached resource from server",
    301                 requestCountAfterFirstLoad > initialRequestCount);
    302         iconListener.drainIconQueue();
    303         loadAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    304         iconListener.waitForNextIcon();
    305         int requestCountAfterSecondLoad = mWebServer.getRequestCount();
    306         assertEquals("Expected to use cache instead of re-fetching resource",
    307                 requestCountAfterSecondLoad, requestCountAfterFirstLoad);
    308     }
    309 
    310     public void testAccessCacheMode_noCache() throws Throwable {
    311         if (!NullWebViewUtils.isWebViewAvailable()) {
    312             return;
    313         }
    314         openIconDatabase();
    315         final IconListenerClient iconListener = new IconListenerClient();
    316         mOnUiThread.setWebChromeClient(iconListener);
    317         startWebServer();
    318 
    319         mSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
    320         assertEquals(WebSettings.LOAD_NO_CACHE, mSettings.getCacheMode());
    321         int initialRequestCount = mWebServer.getRequestCount();
    322         loadAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    323         iconListener.waitForNextIcon();
    324         int requestCountAfterFirstLoad = mWebServer.getRequestCount();
    325         assertTrue("Must fetch non-cached resource from server",
    326                 requestCountAfterFirstLoad > initialRequestCount);
    327         iconListener.drainIconQueue();
    328         loadAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    329         iconListener.waitForNextIcon();
    330         int requestCountAfterSecondLoad = mWebServer.getRequestCount();
    331         assertTrue("Expected to re-fetch resource instead of caching",
    332                 requestCountAfterSecondLoad > requestCountAfterFirstLoad);
    333     }
    334 
    335     public void testAccessCacheMode_cacheOnly() throws Throwable {
    336         if (!NullWebViewUtils.isWebViewAvailable()) {
    337             return;
    338         }
    339         openIconDatabase();
    340         final IconListenerClient iconListener = new IconListenerClient();
    341         mOnUiThread.setWebChromeClient(iconListener);
    342         startWebServer();
    343 
    344         // As a precondition, get the icon in the cache.
    345         mSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
    346         loadAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    347         iconListener.waitForNextIcon();
    348 
    349         mSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
    350         assertEquals(WebSettings.LOAD_CACHE_ONLY, mSettings.getCacheMode());
    351         iconListener.drainIconQueue();
    352         int initialRequestCount = mWebServer.getRequestCount();
    353         loadAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    354         iconListener.waitForNextIcon();
    355         int requestCountAfterFirstLoad = mWebServer.getRequestCount();
    356         assertEquals("Expected to use cache instead of fetching resource",
    357                 requestCountAfterFirstLoad, initialRequestCount);
    358     }
    359 
    360     public void testAccessCursiveFontFamily() throws Exception {
    361         if (!NullWebViewUtils.isWebViewAvailable()) {
    362             return;
    363         }
    364         assertNotNull(mSettings.getCursiveFontFamily());
    365 
    366         String newCusiveFamily = "Apple Chancery";
    367         mSettings.setCursiveFontFamily(newCusiveFamily);
    368         assertEquals(newCusiveFamily, mSettings.getCursiveFontFamily());
    369     }
    370 
    371     public void testAccessFantasyFontFamily() {
    372         if (!NullWebViewUtils.isWebViewAvailable()) {
    373             return;
    374         }
    375         assertNotNull(mSettings.getFantasyFontFamily());
    376 
    377         String newFantasyFamily = "Papyrus";
    378         mSettings.setFantasyFontFamily(newFantasyFamily);
    379         assertEquals(newFantasyFamily, mSettings.getFantasyFontFamily());
    380     }
    381 
    382     public void testAccessFixedFontFamily() {
    383         if (!NullWebViewUtils.isWebViewAvailable()) {
    384             return;
    385         }
    386         assertNotNull(mSettings.getFixedFontFamily());
    387 
    388         String newFixedFamily = "Courier";
    389         mSettings.setFixedFontFamily(newFixedFamily);
    390         assertEquals(newFixedFamily, mSettings.getFixedFontFamily());
    391     }
    392 
    393     public void testAccessSansSerifFontFamily() {
    394         if (!NullWebViewUtils.isWebViewAvailable()) {
    395             return;
    396         }
    397         assertNotNull(mSettings.getSansSerifFontFamily());
    398 
    399         String newFixedFamily = "Verdana";
    400         mSettings.setSansSerifFontFamily(newFixedFamily);
    401         assertEquals(newFixedFamily, mSettings.getSansSerifFontFamily());
    402     }
    403 
    404     public void testAccessSerifFontFamily() {
    405         if (!NullWebViewUtils.isWebViewAvailable()) {
    406             return;
    407         }
    408         assertNotNull(mSettings.getSerifFontFamily());
    409 
    410         String newSerifFamily = "Times";
    411         mSettings.setSerifFontFamily(newSerifFamily);
    412         assertEquals(newSerifFamily, mSettings.getSerifFontFamily());
    413     }
    414 
    415     public void testAccessStandardFontFamily() {
    416         if (!NullWebViewUtils.isWebViewAvailable()) {
    417             return;
    418         }
    419         assertNotNull(mSettings.getStandardFontFamily());
    420 
    421         String newStandardFamily = "Times";
    422         mSettings.setStandardFontFamily(newStandardFamily);
    423         assertEquals(newStandardFamily, mSettings.getStandardFontFamily());
    424     }
    425 
    426     public void testAccessDefaultFontSize() {
    427         if (!NullWebViewUtils.isWebViewAvailable()) {
    428             return;
    429         }
    430         int defaultSize = mSettings.getDefaultFontSize();
    431         assertThat(defaultSize, greaterThan(0));
    432 
    433         mSettings.setDefaultFontSize(1000);
    434         int maxSize = mSettings.getDefaultFontSize();
    435         // cannot check exact size set, since the implementation caps it at an arbitrary limit
    436         assertThat("max size should be greater than default size",
    437                 maxSize,
    438                 greaterThan(defaultSize));
    439 
    440         mSettings.setDefaultFontSize(-10);
    441         int minSize = mSettings.getDefaultFontSize();
    442         assertThat(minSize, greaterThan(0));
    443         assertThat(minSize, lessThan(maxSize));
    444 
    445         mSettings.setDefaultFontSize(10);
    446         assertEquals(10, mSettings.getDefaultFontSize());
    447     }
    448 
    449     public void testAccessDefaultFixedFontSize() {
    450         if (!NullWebViewUtils.isWebViewAvailable()) {
    451             return;
    452         }
    453         int defaultSize = mSettings.getDefaultFixedFontSize();
    454         assertThat(defaultSize, greaterThan(0));
    455 
    456         mSettings.setDefaultFixedFontSize(1000);
    457         int maxSize = mSettings.getDefaultFixedFontSize();
    458         // cannot check exact size set, since the implementation caps it at an arbitrary limit
    459         assertThat("max size should be greater than default size",
    460                 maxSize,
    461                 greaterThan(defaultSize));
    462 
    463         mSettings.setDefaultFixedFontSize(-10);
    464         int minSize = mSettings.getDefaultFixedFontSize();
    465         assertThat(minSize, greaterThan(0));
    466         assertThat(minSize, lessThan(maxSize));
    467 
    468         mSettings.setDefaultFixedFontSize(10);
    469         assertEquals(10, mSettings.getDefaultFixedFontSize());
    470     }
    471 
    472     public void testAccessDefaultTextEncodingName() {
    473         if (!NullWebViewUtils.isWebViewAvailable()) {
    474             return;
    475         }
    476         assertNotNull(mSettings.getDefaultTextEncodingName());
    477 
    478         String newEncodingName = "iso-8859-1";
    479         mSettings.setDefaultTextEncodingName(newEncodingName);
    480         assertEquals(newEncodingName, mSettings.getDefaultTextEncodingName());
    481     }
    482 
    483     public void testAccessJavaScriptCanOpenWindowsAutomatically() throws Exception {
    484         if (!NullWebViewUtils.isWebViewAvailable()) {
    485             return;
    486         }
    487         mSettings.setJavaScriptEnabled(true);
    488         mSettings.setSupportMultipleWindows(true);
    489         startWebServer();
    490 
    491         final WebView childWebView = mOnUiThread.createWebView();
    492         final SettableFuture<Void> createWindowFuture = SettableFuture.create();
    493         mOnUiThread.setWebChromeClient(new WebChromeClient() {
    494             @Override
    495             public boolean onCreateWindow(
    496                     WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
    497                 WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
    498                 transport.setWebView(childWebView);
    499                 resultMsg.sendToTarget();
    500                 createWindowFuture.set(null);
    501                 return true;
    502             }
    503         });
    504 
    505         mSettings.setJavaScriptCanOpenWindowsAutomatically(false);
    506         assertFalse(mSettings.getJavaScriptCanOpenWindowsAutomatically());
    507         mOnUiThread.loadUrl(mWebServer.getAssetUrl(TestHtmlConstants.POPUP_URL));
    508         new PollingCheck(WEBVIEW_TIMEOUT) {
    509             @Override
    510             protected boolean check() {
    511                 return "Popup blocked".equals(mOnUiThread.getTitle());
    512             }
    513         }.run();
    514         assertFalse("onCreateWindow should not have been called yet", createWindowFuture.isDone());
    515 
    516         mSettings.setJavaScriptCanOpenWindowsAutomatically(true);
    517         assertTrue(mSettings.getJavaScriptCanOpenWindowsAutomatically());
    518         mOnUiThread.loadUrl(mWebServer.getAssetUrl(TestHtmlConstants.POPUP_URL));
    519         WebkitUtils.waitForFuture(createWindowFuture);
    520     }
    521 
    522     public void testAccessJavaScriptEnabled() throws Exception {
    523         if (!NullWebViewUtils.isWebViewAvailable()) {
    524             return;
    525         }
    526         mSettings.setJavaScriptEnabled(true);
    527         assertTrue(mSettings.getJavaScriptEnabled());
    528         loadAssetUrl(TestHtmlConstants.JAVASCRIPT_URL);
    529         new PollingCheck(WEBVIEW_TIMEOUT) {
    530             @Override
    531             protected boolean check() {
    532                 return "javascript on".equals(mOnUiThread.getTitle());
    533             }
    534         }.run();
    535 
    536         mSettings.setJavaScriptEnabled(false);
    537         assertFalse(mSettings.getJavaScriptEnabled());
    538         loadAssetUrl(TestHtmlConstants.JAVASCRIPT_URL);
    539         new PollingCheck(WEBVIEW_TIMEOUT) {
    540             @Override
    541             protected boolean check() {
    542                 return "javascript off".equals(mOnUiThread.getTitle());
    543             }
    544         }.run();
    545 
    546     }
    547 
    548     public void testAccessLayoutAlgorithm() {
    549         if (!NullWebViewUtils.isWebViewAvailable()) {
    550             return;
    551         }
    552         assertEquals(WebSettings.LayoutAlgorithm.NARROW_COLUMNS, mSettings.getLayoutAlgorithm());
    553 
    554         mSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
    555         assertEquals(WebSettings.LayoutAlgorithm.NORMAL, mSettings.getLayoutAlgorithm());
    556 
    557         mSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
    558         assertEquals(WebSettings.LayoutAlgorithm.SINGLE_COLUMN, mSettings.getLayoutAlgorithm());
    559     }
    560 
    561     public void testAccessMinimumFontSize() {
    562         if (!NullWebViewUtils.isWebViewAvailable()) {
    563             return;
    564         }
    565         assertEquals(8, mSettings.getMinimumFontSize());
    566 
    567         mSettings.setMinimumFontSize(100);
    568         assertEquals(72, mSettings.getMinimumFontSize());
    569 
    570         mSettings.setMinimumFontSize(-10);
    571         assertEquals(1, mSettings.getMinimumFontSize());
    572 
    573         mSettings.setMinimumFontSize(10);
    574         assertEquals(10, mSettings.getMinimumFontSize());
    575     }
    576 
    577     public void testAccessMinimumLogicalFontSize() {
    578         if (!NullWebViewUtils.isWebViewAvailable()) {
    579             return;
    580         }
    581         assertEquals(8, mSettings.getMinimumLogicalFontSize());
    582 
    583         mSettings.setMinimumLogicalFontSize(100);
    584         assertEquals(72, mSettings.getMinimumLogicalFontSize());
    585 
    586         mSettings.setMinimumLogicalFontSize(-10);
    587         assertEquals(1, mSettings.getMinimumLogicalFontSize());
    588 
    589         mSettings.setMinimumLogicalFontSize(10);
    590         assertEquals(10, mSettings.getMinimumLogicalFontSize());
    591     }
    592 
    593     public void testAccessPluginsEnabled() {
    594         if (!NullWebViewUtils.isWebViewAvailable()) {
    595             return;
    596         }
    597         assertFalse(mSettings.getPluginsEnabled());
    598 
    599         mSettings.setPluginsEnabled(true);
    600         assertTrue(mSettings.getPluginsEnabled());
    601     }
    602 
    603     /**
    604      * This should remain functionally equivalent to
    605      * androidx.webkit.WebSettingsCompatTest#testOffscreenPreRaster. Modifications to this test
    606      * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
    607      */
    608     public void testOffscreenPreRaster() {
    609         if (!NullWebViewUtils.isWebViewAvailable()) {
    610             return;
    611         }
    612         assertFalse(mSettings.getOffscreenPreRaster());
    613 
    614         mSettings.setOffscreenPreRaster(true);
    615         assertTrue(mSettings.getOffscreenPreRaster());
    616     }
    617 
    618     public void testAccessPluginsPath() {
    619         if (!NullWebViewUtils.isWebViewAvailable()) {
    620             return;
    621         }
    622         assertNotNull(mSettings.getPluginsPath());
    623 
    624         String pluginPath = "pluginPath";
    625         mSettings.setPluginsPath(pluginPath);
    626         assertEquals("Plugin path always empty", "", mSettings.getPluginsPath());
    627     }
    628 
    629     public void testAccessTextSize() {
    630         if (!NullWebViewUtils.isWebViewAvailable()) {
    631             return;
    632         }
    633         mSettings.setTextSize(TextSize.NORMAL);
    634         assertEquals(TextSize.NORMAL, mSettings.getTextSize());
    635 
    636         mSettings.setTextSize(TextSize.LARGER);
    637         assertEquals(TextSize.LARGER, mSettings.getTextSize());
    638 
    639         mSettings.setTextSize(TextSize.LARGEST);
    640         assertEquals(TextSize.LARGEST, mSettings.getTextSize());
    641 
    642         mSettings.setTextSize(TextSize.SMALLER);
    643         assertEquals(TextSize.SMALLER, mSettings.getTextSize());
    644 
    645         mSettings.setTextSize(TextSize.SMALLEST);
    646         assertEquals(TextSize.SMALLEST, mSettings.getTextSize());
    647     }
    648 
    649     public void testAccessUseDoubleTree() {
    650         if (!NullWebViewUtils.isWebViewAvailable()) {
    651             return;
    652         }
    653         assertFalse(mSettings.getUseDoubleTree());
    654 
    655         mSettings.setUseDoubleTree(true);
    656         assertFalse("setUseDoubleTree should be a no-op", mSettings.getUseDoubleTree());
    657     }
    658 
    659     public void testAccessUseWideViewPort() {
    660         if (!NullWebViewUtils.isWebViewAvailable()) {
    661             return;
    662         }
    663         assertFalse(mSettings.getUseWideViewPort());
    664 
    665         mSettings.setUseWideViewPort(true);
    666         assertTrue(mSettings.getUseWideViewPort());
    667     }
    668 
    669     public void testSetNeedInitialFocus() {
    670         if (!NullWebViewUtils.isWebViewAvailable()) {
    671             return;
    672         }
    673         mSettings.setNeedInitialFocus(false);
    674 
    675         mSettings.setNeedInitialFocus(true);
    676     }
    677 
    678     public void testSetRenderPriority() {
    679         if (!NullWebViewUtils.isWebViewAvailable()) {
    680             return;
    681         }
    682         mSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);
    683 
    684         mSettings.setRenderPriority(WebSettings.RenderPriority.LOW);
    685 
    686         mSettings.setRenderPriority(WebSettings.RenderPriority.NORMAL);
    687     }
    688 
    689     public void testAccessSupportMultipleWindows() {
    690         if (!NullWebViewUtils.isWebViewAvailable()) {
    691             return;
    692         }
    693         assertFalse(mSettings.supportMultipleWindows());
    694 
    695         mSettings.setSupportMultipleWindows(true);
    696         assertTrue(mSettings.supportMultipleWindows());
    697     }
    698 
    699     public void testAccessSupportZoom() throws Throwable {
    700         if (!NullWebViewUtils.isWebViewAvailable()) {
    701             return;
    702         }
    703         assertTrue(mSettings.supportZoom());
    704 
    705         mSettings.setSupportZoom(false);
    706         assertFalse(mSettings.supportZoom());
    707     }
    708 
    709     public void testAccessBuiltInZoomControls() throws Throwable {
    710         if (!NullWebViewUtils.isWebViewAvailable()) {
    711             return;
    712         }
    713         assertFalse(mSettings.getBuiltInZoomControls());
    714 
    715         mSettings.setBuiltInZoomControls(true);
    716         assertTrue(mSettings.getBuiltInZoomControls());
    717     }
    718 
    719     public void testAppCacheDisabled() throws Throwable {
    720         if (!NullWebViewUtils.isWebViewAvailable()) {
    721             return;
    722         }
    723         // Test that when AppCache is disabled, we don't get any AppCache
    724         // callbacks.
    725         startWebServer();
    726         final String url = mWebServer.getAppCacheUrl();
    727         mSettings.setJavaScriptEnabled(true);
    728 
    729         mOnUiThread.loadUrlAndWaitForCompletion(url);
    730         new PollingCheck(WEBVIEW_TIMEOUT) {
    731             protected boolean check() {
    732                 return "Loaded".equals(mOnUiThread.getTitle());
    733             }
    734         }.run();
    735         // The page is now loaded. Wait for a further 1s to check no AppCache
    736         // callbacks occur.
    737         Thread.sleep(1000);
    738         assertEquals("Loaded", mOnUiThread.getTitle());
    739     }
    740 
    741     public void testAppCacheEnabled() throws Throwable {
    742         if (!NullWebViewUtils.isWebViewAvailable()) {
    743             return;
    744         }
    745         // Note that the AppCache path can only be set once. This limits the
    746         // amount of testing we can do, and means that we must test all aspects
    747         // of setting the AppCache path in a single test to guarantee ordering.
    748 
    749         // Test that when AppCache is enabled but no valid path is provided,
    750         // we don't get any AppCache callbacks.
    751         startWebServer();
    752         final String url = mWebServer.getAppCacheUrl();
    753         mSettings.setAppCacheEnabled(true);
    754         mSettings.setJavaScriptEnabled(true);
    755 
    756         mOnUiThread.loadUrlAndWaitForCompletion(url);
    757         new PollingCheck(WEBVIEW_TIMEOUT) {
    758             @Override
    759             protected boolean check() {
    760                 return "Loaded".equals(mOnUiThread.getTitle());
    761             }
    762         }.run();
    763         // The page is now loaded. Wait for a further 1s to check no AppCache
    764         // callbacks occur.
    765         Thread.sleep(1000);
    766         assertEquals("Loaded", mOnUiThread.getTitle());
    767 
    768         // Test that when AppCache is enabled and a valid path is provided, we
    769         // get an AppCache callback of some kind.
    770         mSettings.setAppCachePath(getActivity().getDir("appcache", 0).getPath());
    771         mOnUiThread.loadUrlAndWaitForCompletion(url);
    772         new PollingCheck(WEBVIEW_TIMEOUT) {
    773             @Override
    774             protected boolean check() {
    775                 return mOnUiThread.getTitle() != null
    776                         && mOnUiThread.getTitle().endsWith("Callback");
    777             }
    778         }.run();
    779     }
    780 
    781     // Ideally, we need a test case for the enabled case. However, it seems that
    782     // enabling the database should happen prior to navigating the first url due to
    783     // some internal limitations of webview. For this reason, we only provide a
    784     // test case for "disabled" behavior.
    785     // Also loading as data rather than using URL should work, but it causes a
    786     // security exception in JS, most likely due to cross domain access. So we load
    787     // using a URL. Finally, it looks like enabling database requires creating a
    788     // webChromeClient and listening to Quota callbacks, which is not documented.
    789     public void testDatabaseDisabled() throws Throwable {
    790         if (!NullWebViewUtils.isWebViewAvailable()) {
    791             return;
    792         }
    793         // Verify that websql database does not work when disabled.
    794         startWebServer();
    795 
    796         mOnUiThread.setWebChromeClient(new WebViewSyncLoader.WaitForProgressClient(mOnUiThread) {
    797             @Override
    798             public void onExceededDatabaseQuota(String url, String databaseId, long quota,
    799                 long estimatedSize, long total, WebStorage.QuotaUpdater updater) {
    800                 updater.updateQuota(estimatedSize);
    801             }
    802         });
    803         mSettings.setJavaScriptEnabled(true);
    804         mSettings.setDatabaseEnabled(false);
    805         final String url = mWebServer.getAssetUrl(TestHtmlConstants.DATABASE_ACCESS_URL);
    806         mOnUiThread.loadUrlAndWaitForCompletion(url);
    807         assertEquals("No database", mOnUiThread.getTitle());
    808     }
    809 
    810     /**
    811      * This should remain functionally equivalent to
    812      * androidx.webkit.WebSettingsCompatTest#testDisabledActionModeMenuItems. Modifications to this
    813      * test should be reflected in that test as necessary. See http://go/modifying-webview-cts.
    814      */
    815     public void testDisabledActionModeMenuItems() throws Throwable {
    816         if (!NullWebViewUtils.isWebViewAvailable()) {
    817             return;
    818         }
    819 
    820         assertEquals(WebSettings.MENU_ITEM_NONE, mSettings.getDisabledActionModeMenuItems());
    821 
    822         int allDisabledFlags = WebSettings.MENU_ITEM_NONE | WebSettings.MENU_ITEM_SHARE |
    823                 WebSettings.MENU_ITEM_WEB_SEARCH | WebSettings.MENU_ITEM_PROCESS_TEXT;
    824         for (int i = WebSettings.MENU_ITEM_NONE; i <= allDisabledFlags; i++) {
    825             mSettings.setDisabledActionModeMenuItems(i);
    826             assertEquals(i, mSettings.getDisabledActionModeMenuItems());
    827         }
    828     }
    829 
    830     public void testLoadsImagesAutomatically_default() throws Throwable {
    831         if (!NullWebViewUtils.isWebViewAvailable()) {
    832             return;
    833         }
    834         assertTrue(mSettings.getLoadsImagesAutomatically());
    835     }
    836 
    837     public void testLoadsImagesAutomatically_httpImagesLoaded() throws Throwable {
    838         if (!NullWebViewUtils.isWebViewAvailable()) {
    839             return;
    840         }
    841         startWebServer();
    842         mSettings.setJavaScriptEnabled(true);
    843         mSettings.setLoadsImagesAutomatically(true);
    844 
    845         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
    846         assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
    847     }
    848 
    849     public void testLoadsImagesAutomatically_dataUriImagesLoaded() throws Throwable {
    850         if (!NullWebViewUtils.isWebViewAvailable()) {
    851             return;
    852         }
    853         startWebServer();
    854         mSettings.setJavaScriptEnabled(true);
    855         mSettings.setLoadsImagesAutomatically(true);
    856 
    857         mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
    858         assertEquals(DATA_URL_IMAGE_HEIGHT, mOnUiThread.getTitle());
    859     }
    860 
    861     public void testLoadsImagesAutomatically_blockLoadingImages() throws Throwable {
    862         if (!NullWebViewUtils.isWebViewAvailable()) {
    863             return;
    864         }
    865         startWebServer();
    866         mSettings.setJavaScriptEnabled(true);
    867         mSettings.setLoadsImagesAutomatically(false);
    868 
    869         mOnUiThread.clearCache(true); // in case of side-effects from other tests
    870         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
    871         assertEquals(EMPTY_IMAGE_HEIGHT, mOnUiThread.getTitle());
    872 
    873         mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
    874         assertEquals(EMPTY_IMAGE_HEIGHT, mOnUiThread.getTitle());
    875     }
    876 
    877     public void testLoadsImagesAutomatically_loadImagesWithoutReload() throws Throwable {
    878         if (!NullWebViewUtils.isWebViewAvailable()) {
    879             return;
    880         }
    881         startWebServer();
    882         mSettings.setJavaScriptEnabled(true);
    883         mSettings.setLoadsImagesAutomatically(false);
    884 
    885         mOnUiThread.clearCache(true); // in case of side-effects from other tests
    886         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
    887         assertEquals(EMPTY_IMAGE_HEIGHT, mOnUiThread.getTitle());
    888         mSettings.setLoadsImagesAutomatically(true); // load images, without calling #reload()
    889         waitForNonEmptyImage();
    890         assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
    891 
    892         mSettings.setLoadsImagesAutomatically(false);
    893         mOnUiThread.clearCache(true);
    894         mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
    895         assertEquals(EMPTY_IMAGE_HEIGHT, mOnUiThread.getTitle());
    896         mSettings.setLoadsImagesAutomatically(true); // load images, without calling #reload()
    897         waitForNonEmptyImage();
    898         assertEquals(DATA_URL_IMAGE_HEIGHT, mOnUiThread.getTitle());
    899     }
    900 
    901     public void testBlockNetworkImage() throws Throwable {
    902         if (!NullWebViewUtils.isWebViewAvailable()) {
    903             return;
    904         }
    905         assertFalse(mSettings.getBlockNetworkImage());
    906 
    907         startWebServer();
    908         mSettings.setJavaScriptEnabled(true);
    909 
    910         // Check that by default network and data url images are loaded.
    911         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
    912         assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
    913         mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
    914         assertEquals(DATA_URL_IMAGE_HEIGHT, mOnUiThread.getTitle());
    915 
    916         // Check that only network images are blocked, data url images are still loaded.
    917         // Also check that network images are loaded automatically once we disable the setting,
    918         // without reloading the page.
    919         mSettings.setBlockNetworkImage(true);
    920         mOnUiThread.clearCache(true);
    921         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
    922         assertEquals(EMPTY_IMAGE_HEIGHT, mOnUiThread.getTitle());
    923         mSettings.setBlockNetworkImage(false);
    924         waitForNonEmptyImage();
    925         assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
    926 
    927         mSettings.setBlockNetworkImage(true);
    928         mOnUiThread.clearCache(true);
    929         mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
    930         assertEquals(DATA_URL_IMAGE_HEIGHT, mOnUiThread.getTitle());
    931     }
    932 
    933     public void testBlockNetworkLoads() throws Throwable {
    934         if (!NullWebViewUtils.isWebViewAvailable()) {
    935             return;
    936         }
    937         assertFalse(mSettings.getBlockNetworkLoads());
    938 
    939         startWebServer();
    940         mSettings.setJavaScriptEnabled(true);
    941 
    942         // Check that by default network resources and data url images are loaded.
    943         mOnUiThread.loadUrlAndWaitForCompletion(
    944             mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
    945         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
    946         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
    947         assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
    948         mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
    949         assertEquals(DATA_URL_IMAGE_HEIGHT, mOnUiThread.getTitle());
    950 
    951         // Check that only network resources are blocked, data url images are still loaded.
    952         mSettings.setBlockNetworkLoads(true);
    953         mOnUiThread.clearCache(true);
    954         mOnUiThread.loadUrlAndWaitForCompletion(
    955             mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
    956         assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
    957         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
    958         assertEquals(EMPTY_IMAGE_HEIGHT, mOnUiThread.getTitle());
    959         mOnUiThread.loadDataAndWaitForCompletion(DATA_URL_IMAGE_HTML, "text/html", null);
    960         assertEquals(DATA_URL_IMAGE_HEIGHT, mOnUiThread.getTitle());
    961 
    962         // Check that network resources are loaded once we disable the setting and reload the page.
    963         mSettings.setBlockNetworkLoads(false);
    964         mOnUiThread.loadUrlAndWaitForCompletion(
    965             mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
    966         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
    967         mOnUiThread.loadDataAndWaitForCompletion(getNetworkImageHtml(), "text/html", null);
    968         assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
    969     }
    970 
    971     // Verify that an image in local file system can be loaded by an asset
    972     public void testLocalImageLoads() throws Throwable {
    973         if (!NullWebViewUtils.isWebViewAvailable()) {
    974             return;
    975         }
    976 
    977         mSettings.setJavaScriptEnabled(true);
    978         // Check that local images are loaded without issues regardless of domain checkings
    979         mSettings.setAllowUniversalAccessFromFileURLs(false);
    980         mSettings.setAllowFileAccessFromFileURLs(false);
    981         String url = TestHtmlConstants.getFileUrl(TestHtmlConstants.IMAGE_ACCESS_URL);
    982         mOnUiThread.loadUrlAndWaitForCompletion(url);
    983         waitForNonEmptyImage();
    984         assertEquals(NETWORK_IMAGE_HEIGHT, mOnUiThread.getTitle());
    985     }
    986 
    987     // Verify that javascript cross-domain request permissions matches file domain settings
    988     // for iframes
    989     public void testIframesWhenAccessFromFileURLsEnabled() throws Throwable {
    990         if (!NullWebViewUtils.isWebViewAvailable()) {
    991             return;
    992         }
    993 
    994         mSettings.setJavaScriptEnabled(true);
    995         // disable universal access from files
    996         mSettings.setAllowUniversalAccessFromFileURLs(false);
    997         mSettings.setAllowFileAccessFromFileURLs(true);
    998 
    999         // when cross file scripting is enabled, make sure cross domain requests succeed
   1000         String url = TestHtmlConstants.getFileUrl(TestHtmlConstants.IFRAME_ACCESS_URL);
   1001         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1002         String iframeUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1003         assertEquals(iframeUrl, mOnUiThread.getTitle());
   1004     }
   1005 
   1006     // Verify that javascript cross-domain request permissions matches file domain settings
   1007     // for iframes
   1008     public void testIframesWhenAccessFromFileURLsDisabled() throws Throwable {
   1009         if (!NullWebViewUtils.isWebViewAvailable()) {
   1010             return;
   1011         }
   1012 
   1013         mSettings.setJavaScriptEnabled(true);
   1014         // disable universal access from files
   1015         mSettings.setAllowUniversalAccessFromFileURLs(false);
   1016         mSettings.setAllowFileAccessFromFileURLs(false);
   1017 
   1018         // when cross file scripting is disabled, make sure cross domain requests fail
   1019         String url = TestHtmlConstants.getFileUrl(TestHtmlConstants.IFRAME_ACCESS_URL);
   1020         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1021         String iframeUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1022         assertFalse("Title should not have changed, because file URL access is disabled",
   1023                 iframeUrl.equals(mOnUiThread.getTitle()));
   1024     }
   1025 
   1026     // Verify that enabling file access from file URLs enable XmlHttpRequest (XHR) across files
   1027     public void testXHRWhenAccessFromFileURLsEnabled() throws Throwable {
   1028         if (!NullWebViewUtils.isWebViewAvailable()) {
   1029             return;
   1030         }
   1031         verifyFileXHR(true);
   1032     }
   1033 
   1034     // Verify that disabling file access from file URLs disable XmlHttpRequest (XHR) accross files
   1035     public void testXHRWhenAccessFromFileURLsDisabled() throws Throwable {
   1036         if (!NullWebViewUtils.isWebViewAvailable()) {
   1037             return;
   1038         }
   1039 
   1040         verifyFileXHR(false);
   1041     }
   1042 
   1043     // verify XHR across files matches the allowFileAccessFromFileURLs setting
   1044     private void verifyFileXHR(boolean enableXHR) throws Throwable {
   1045         // target file content
   1046         String target ="<html><body>target</body><html>";
   1047 
   1048         String targetPath = mContext.getFileStreamPath("target.html").getAbsolutePath();
   1049         // local file content that use XHR to read the target file
   1050         String local ="" +
   1051             "<html><body><script>" +
   1052             "var client = new XMLHttpRequest();" +
   1053             "client.open('GET', 'file://" + targetPath + "',false);" +
   1054             "client.send();" +
   1055             "document.title = client.responseText;" +
   1056             "</script></body></html>";
   1057 
   1058         // create files in internal storage
   1059         writeFile("local.html", local);
   1060         writeFile("target.html", target);
   1061 
   1062         mSettings.setJavaScriptEnabled(true);
   1063         // disable universal access from files
   1064         mSettings.setAllowUniversalAccessFromFileURLs(false);
   1065         mSettings.setAllowFileAccessFromFileURLs(enableXHR);
   1066         String localPath = mContext.getFileStreamPath("local.html").getAbsolutePath();
   1067         // when cross file scripting is enabled, make sure cross domain requests succeed
   1068         mOnUiThread.loadUrlAndWaitForCompletion("file://" + localPath);
   1069         if (enableXHR) {
   1070             assertEquals("Expected title to change, because XHR should be enabled", target,
   1071                     mOnUiThread.getTitle());
   1072         } else {
   1073             assertFalse("Title should not have changed, because XHR should be disabled",
   1074                     target.equals(mOnUiThread.getTitle()));
   1075         }
   1076     }
   1077 
   1078     // Create a private file on internal storage from the given string
   1079     private void writeFile(String filename, String content) throws Exception {
   1080 
   1081         FileOutputStream fos = mContext.openFileOutput(filename, Context.MODE_PRIVATE);
   1082         fos.write(content.getBytes());
   1083         fos.close();
   1084     }
   1085 
   1086     public void testAllowMixedMode() throws Throwable {
   1087         if (!NullWebViewUtils.isWebViewAvailable()) {
   1088             return;
   1089         }
   1090 
   1091         final String INSECURE_BASE_URL = "http://www.example.com/";
   1092         final String INSECURE_JS_URL = INSECURE_BASE_URL + "insecure.js";
   1093         final String INSECURE_IMG_URL = INSECURE_BASE_URL + "insecure.png";
   1094         final String SECURE_URL = "/secure.html";
   1095         final String JS_HTML = "<script src=\"" + INSECURE_JS_URL + "\"></script>";
   1096         final String IMG_HTML = "<img src=\"" + INSECURE_IMG_URL + "\" />";
   1097         final String SECURE_HTML = "<body>" + IMG_HTML + " " + JS_HTML + "</body>";
   1098         final String JS_CONTENT = "window.loaded_js = 42;";
   1099         final String IMG_CONTENT = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
   1100 
   1101         final class InterceptClient extends WaitForLoadedClient {
   1102             public int mInsecureJsCounter;
   1103             public int mInsecureImgCounter;
   1104 
   1105             public InterceptClient() {
   1106                 super(mOnUiThread);
   1107             }
   1108 
   1109             @Override
   1110             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   1111                 handler.proceed();
   1112             }
   1113 
   1114             @Override
   1115             public WebResourceResponse shouldInterceptRequest(
   1116                     WebView view, WebResourceRequest request) {
   1117                 if (request.getUrl().toString().equals(INSECURE_JS_URL)) {
   1118                     mInsecureJsCounter++;
   1119                     return new WebResourceResponse("text/javascript", "utf-8",
   1120                         new ByteArrayInputStream(JS_CONTENT.getBytes(StandardCharsets.UTF_8)));
   1121                 } else if (request.getUrl().toString().equals(INSECURE_IMG_URL)) {
   1122                     mInsecureImgCounter++;
   1123                     return new WebResourceResponse("image/gif", "utf-8",
   1124                         new ByteArrayInputStream(Base64.decode(IMG_CONTENT, Base64.DEFAULT)));
   1125                 }
   1126 
   1127                 if (request.getUrl().toString().startsWith(INSECURE_BASE_URL)) {
   1128                     return new WebResourceResponse("text/html", "UTF-8", null);
   1129                 }
   1130                 return null;
   1131             }
   1132         }
   1133 
   1134         InterceptClient interceptClient = new InterceptClient();
   1135         mOnUiThread.setWebViewClient(interceptClient);
   1136         mSettings.setJavaScriptEnabled(true);
   1137         TestWebServer httpsServer = null;
   1138         try {
   1139             httpsServer = new TestWebServer(true);
   1140             String secureUrl = httpsServer.setResponse(SECURE_URL, SECURE_HTML, null);
   1141             mOnUiThread.clearSslPreferences();
   1142 
   1143             mSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
   1144             mOnUiThread.loadUrlAndWaitForCompletion(secureUrl);
   1145             assertEquals(1, httpsServer.getRequestCount(SECURE_URL));
   1146             assertEquals(0, interceptClient.mInsecureJsCounter);
   1147             assertEquals(0, interceptClient.mInsecureImgCounter);
   1148 
   1149             mSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
   1150             mOnUiThread.loadUrlAndWaitForCompletion(secureUrl);
   1151             assertEquals(2, httpsServer.getRequestCount(SECURE_URL));
   1152             assertEquals(1, interceptClient.mInsecureJsCounter);
   1153             assertEquals(1, interceptClient.mInsecureImgCounter);
   1154 
   1155             mSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
   1156             mOnUiThread.loadUrlAndWaitForCompletion(secureUrl);
   1157             assertEquals(3, httpsServer.getRequestCount(SECURE_URL));
   1158             assertEquals(1, interceptClient.mInsecureJsCounter);
   1159             assertEquals(2, interceptClient.mInsecureImgCounter);
   1160         } finally {
   1161             if (httpsServer != null) {
   1162                 httpsServer.shutdown();
   1163             }
   1164         }
   1165     }
   1166 
   1167     /**
   1168      * This should remain functionally equivalent to
   1169      * androidx.webkit.WebSettingsCompatTest#testEnableSafeBrowsing. Modifications to this test
   1170      * should be reflected in that test as necessary. See http://go/modifying-webview-cts.
   1171      */
   1172     public void testEnableSafeBrowsing() throws Throwable {
   1173         if (!NullWebViewUtils.isWebViewAvailable()) {
   1174             return;
   1175         }
   1176         mSettings.setSafeBrowsingEnabled(false);
   1177         assertFalse(mSettings.getSafeBrowsingEnabled());
   1178     }
   1179 
   1180     private  int[] getBitmapPixels(Bitmap bitmap, int x, int y, int width, int height) {
   1181         int[] pixels = new int[width * height];
   1182         bitmap.getPixels(pixels, 0, width, x, y, width, height);
   1183         return pixels;
   1184     }
   1185 
   1186     private Map<Integer,Integer> getBitmapHistogram(Bitmap bitmap, int x, int y, int width, int height) {
   1187         HashMap<Integer, Integer> histogram = new HashMap();
   1188         for (int pixel : getBitmapPixels(bitmap, x, y, width, height)) {
   1189             histogram.put(pixel, histogram.getOrDefault(pixel, 0) + 1);
   1190         }
   1191         return histogram;
   1192     }
   1193 
   1194     public void testForceDark_default() throws Throwable {
   1195         if (!NullWebViewUtils.isWebViewAvailable()) {
   1196             return;
   1197         }
   1198 
   1199         assertEquals("The default force dark state should be AUTO",
   1200                 mSettings.getForceDark(), WebSettings.FORCE_DARK_AUTO);
   1201     }
   1202 
   1203     private void setWebViewSize(int width, int height) {
   1204         // Set the webview size to 64x64
   1205         WebkitUtils.onMainThreadSync(() -> {
   1206             WebView webView = mOnUiThread.getWebView();
   1207             ViewGroup.LayoutParams params = webView.getLayoutParams();
   1208             params.height = height;
   1209             params.width = width;
   1210             webView.setLayoutParams(params);
   1211         });
   1212 
   1213     }
   1214 
   1215     public void testForceDark_rendersDark() throws Throwable {
   1216         if (!NullWebViewUtils.isWebViewAvailable()) {
   1217             return;
   1218         }
   1219 
   1220         setWebViewSize(64, 64);
   1221 
   1222         Map<Integer, Integer> histogram;
   1223         Integer[] colourValues;
   1224 
   1225         // Loading about:blank into a force-dark-on webview should result in a dark background
   1226         mSettings.setForceDark(WebSettings.FORCE_DARK_ON);
   1227         assertEquals("Force dark should have been set to ON",
   1228                 mSettings.getForceDark(), WebSettings.FORCE_DARK_ON);
   1229 
   1230         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
   1231         histogram = getBitmapHistogram(mOnUiThread.captureBitmap(), 0, 0, 64, 64);
   1232         assertEquals("Bitmap should have a single colour", histogram.size(), 1);
   1233         colourValues = histogram.keySet().toArray(new Integer[0]);
   1234         assertThat("Bitmap colour should be dark",
   1235                 Color.luminance(colourValues[0]), lessThan(0.5f));
   1236 
   1237         // Loading about:blank into a force-dark-off webview should result in a light background
   1238         mSettings.setForceDark(WebSettings.FORCE_DARK_OFF);
   1239         assertEquals("Force dark should have been set to OFF",
   1240                 mSettings.getForceDark(), WebSettings.FORCE_DARK_OFF);
   1241 
   1242         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
   1243         histogram = getBitmapHistogram(mOnUiThread.captureBitmap(), 0, 0, 64, 64);
   1244         assertEquals("Bitmap should have a single colour", histogram.size(), 1);
   1245         colourValues = histogram.keySet().toArray(new Integer[0]);
   1246         assertThat("Bitmap colour should be light",
   1247                 Color.luminance(colourValues[0]), greaterThan(0.5f));
   1248     }
   1249 
   1250     /**
   1251      * Starts the internal web server. The server will be shut down automatically
   1252      * during tearDown().
   1253      *
   1254      * @throws Exception
   1255      */
   1256     private void startWebServer() throws Exception {
   1257         assertNull(mWebServer);
   1258         mWebServer = new CtsTestServer(getActivity(), false);
   1259     }
   1260 
   1261     /**
   1262      * Load the given asset from the internal web server. Starts the server if
   1263      * it is not already running.
   1264      *
   1265      * @param asset The name of the asset to load.
   1266      * @throws Exception
   1267      */
   1268     private void loadAssetUrl(String asset) throws Exception {
   1269         if (mWebServer == null) {
   1270             startWebServer();
   1271         }
   1272         String url = mWebServer.getAssetUrl(asset);
   1273         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1274     }
   1275 
   1276     private String getNetworkImageHtml() {
   1277         return "<html>" +
   1278                 "<head><script>function updateTitle(){" +
   1279                 "document.title=document.getElementById('img').naturalHeight;}</script></head>" +
   1280                 "<body onload='updateTitle()'>" +
   1281                 "<img id='img' onload='updateTitle()' src='" +
   1282                 mWebServer.getAssetUrl(TestHtmlConstants.SMALL_IMG_URL) +
   1283                 "'></body></html>";
   1284     }
   1285 
   1286     private void waitForNonEmptyImage() {
   1287         new PollingCheck(WEBVIEW_TIMEOUT) {
   1288             @Override
   1289             protected boolean check() {
   1290                 return !EMPTY_IMAGE_HEIGHT.equals(mOnUiThread.getTitle());
   1291             }
   1292         }.run();
   1293     }
   1294 
   1295     private class IconListenerClient extends WaitForProgressClient {
   1296         private final BlockingQueue<Bitmap> mReceivedIconQueue = new LinkedBlockingQueue<>();
   1297 
   1298         public IconListenerClient() {
   1299             super(mOnUiThread);
   1300         }
   1301 
   1302         @Override
   1303         public void onReceivedIcon(WebView view, Bitmap icon) {
   1304             mReceivedIconQueue.add(icon);
   1305         }
   1306 
   1307         /**
   1308          * Exposed as a precaution, in case for some reason we get multiple calls to
   1309          * {@link #onReceivedIcon}.
   1310          */
   1311         public void drainIconQueue() {
   1312             while (mReceivedIconQueue.poll() != null) {}
   1313         }
   1314 
   1315         public void waitForNextIcon() {
   1316             // TODO(ntfschr): consider exposing the Bitmap, if we want to make assertions.
   1317             WebkitUtils.waitForNextQueueElement(mReceivedIconQueue);
   1318         }
   1319     }
   1320 }
   1321