root/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ensureSettingHasAlteredValue
  2. ensureSettingHasInitialValue
  3. setAlteredSettingValue
  4. setInitialSettingValue
  5. getAlteredValue
  6. getInitialValue
  7. getCurrentValue
  8. setCurrentValue
  9. doEnsureSettingHasValue
  10. getTitleOnUiThread
  11. loadDataSync
  12. loadUrlSync
  13. loadUrlSyncAndExpectError
  14. executeJavaScriptAndWaitForResult
  15. ensureSettingHasValue
  16. getAlteredValue
  17. getInitialValue
  18. getCurrentValue
  19. setCurrentValue
  20. doEnsureSettingHasValue
  21. getData
  22. doEnsureSettingHasValue
  23. getScript
  24. getAlteredValue
  25. getInitialValue
  26. getCurrentValue
  27. setCurrentValue
  28. doEnsureSettingHasValue
  29. getData
  30. getAlteredValue
  31. getInitialValue
  32. getCurrentValue
  33. setCurrentValue
  34. doEnsureSettingHasValue
  35. getData
  36. getAlteredValue
  37. getInitialValue
  38. getCurrentValue
  39. setCurrentValue
  40. doEnsureSettingHasValue
  41. getData
  42. getAlteredValue
  43. getInitialValue
  44. getCurrentValue
  45. setCurrentValue
  46. doEnsureSettingHasValue
  47. getAlteredValue
  48. getInitialValue
  49. getCurrentValue
  50. setCurrentValue
  51. doEnsureSettingHasValue
  52. getAlteredValue
  53. getInitialValue
  54. getCurrentValue
  55. setCurrentValue
  56. doEnsureSettingHasValue
  57. getData
  58. getAlteredValue
  59. getInitialValue
  60. getCurrentValue
  61. setCurrentValue
  62. doEnsureSettingHasValue
  63. getData
  64. getAlteredValue
  65. getInitialValue
  66. getCurrentValue
  67. setCurrentValue
  68. doEnsureSettingHasValue
  69. getAlteredValue
  70. getInitialValue
  71. getCurrentValue
  72. setCurrentValue
  73. doEnsureSettingHasValue
  74. getAlteredValue
  75. getInitialValue
  76. getCurrentValue
  77. setCurrentValue
  78. doEnsureSettingHasValue
  79. getAlteredValue
  80. getInitialValue
  81. getCurrentValue
  82. setCurrentValue
  83. doEnsureSettingHasValue
  84. getAlteredValue
  85. getInitialValue
  86. getCurrentValue
  87. setCurrentValue
  88. doEnsureSettingHasValue
  89. getAlteredValue
  90. getInitialValue
  91. getCurrentValue
  92. setCurrentValue
  93. doEnsureSettingHasValue
  94. getAlteredValue
  95. getInitialValue
  96. getCurrentValue
  97. setCurrentValue
  98. doEnsureSettingHasValue
  99. getAlteredValue
  100. getInitialValue
  101. getCurrentValue
  102. setCurrentValue
  103. doEnsureSettingHasValue
  104. setCurrentValue
  105. getActualFontSize
  106. getData
  107. getAlteredValue
  108. getInitialValue
  109. getCurrentValue
  110. setCurrentValue
  111. doEnsureSettingHasValue
  112. getAlteredValue
  113. getInitialValue
  114. getCurrentValue
  115. setCurrentValue
  116. doEnsureSettingHasValue
  117. getAlteredValue
  118. getInitialValue
  119. getCurrentValue
  120. setCurrentValue
  121. doEnsureSettingHasValue
  122. getAlteredValue
  123. getInitialValue
  124. getCurrentValue
  125. setCurrentValue
  126. doEnsureSettingHasValue
  127. getData
  128. getAlteredValue
  129. getInitialValue
  130. getCurrentValue
  131. setCurrentValue
  132. doEnsureSettingHasValue
  133. getAlteredValue
  134. getInitialValue
  135. getCurrentValue
  136. setCurrentValue
  137. doEnsureSettingHasValue
  138. getData
  139. getAlteredValue
  140. getInitialValue
  141. getCurrentValue
  142. setCurrentValue
  143. doEnsureSettingHasValue
  144. getData
  145. Feature
  146. testJavaScriptDisabledByDefault
  147. Feature
  148. testJavaScriptEnabledWithTwoViews
  149. Feature
  150. testJavaScriptEnabledDynamicWithTwoViews
  151. Feature
  152. testPluginsEnabledWithTwoViews
  153. Feature
  154. testStandardFontFamilyWithTwoViews
  155. Feature
  156. testDefaultFontSizeWithTwoViews
  157. Feature
  158. testLoadsImagesAutomaticallyNoPageReload
  159. Feature
  160. testLoadsImagesAutomaticallyWithTwoViews
  161. Feature
  162. testDefaultTextEncodingWithTwoViews
  163. Feature
  164. testUserAgentStringDefault
  165. Feature
  166. testUserAgentStringOverride
  167. Feature
  168. testUserAgentStringOverrideForHistory
  169. Feature
  170. testUserAgentStringWithTwoViews
  171. Feature
  172. testUserAgentWithTestServer
  173. Feature
  174. testDomStorageEnabledWithTwoViews
  175. Feature
  176. testDatabaseInitialValue
  177. Feature
  178. testDatabaseEnabled
  179. Feature
  180. testDatabaseDisabled
  181. Feature
  182. testUniversalAccessFromFilesWithTwoViews
  183. Feature
  184. testFileAccessFromFilesImage
  185. Feature
  186. testFileAccessFromFilesIframeWithTwoViews
  187. Feature
  188. testFileAccessFromFilesXhrWithTwoViews
  189. Feature
  190. testFileUrlAccessWithTwoViews
  191. Feature
  192. testContentUrlAccessWithTwoViews
  193. Feature
  194. testBlockingContentUrlsFromDataUrls
  195. Feature
  196. testContentUrlFromFileWithTwoViews
  197. Feature
  198. testBlockNetworkImagesDoesNotBlockDataUrlImage
  199. Feature
  200. testBlockNetworkImagesBlocksNetworkImageAndReloadInPlace
  201. Feature
  202. testBlockNetworkImagesWithTwoViews
  203. Feature
  204. testBlockNetworkLoadsWithHttpResources
  205. onCanPlay
  206. onError
  207. Feature
  208. testBlockNetworkLoadsWithAudio
  209. Feature
  210. testAssetUrl
  211. Feature
  212. testResourceUrl
  213. Feature
  214. testFileUrlAccessToggleDoesNotBlockAssetUrls
  215. Feature
  216. testFileUrlAccessToggleDoesNotBlockResourceUrls
  217. Feature
  218. testLayoutAlgorithmWithTwoViews
  219. Feature
  220. testTextZoomWithTwoViews
  221. Feature
  222. testTextZoomAutosizingWithTwoViews
  223. Feature
  224. testJavaScriptPopupsWithTwoViews
  225. Feature
  226. testCacheMode
  227. Feature
  228. testCacheModeWithBlockedNetworkLoads
  229. Feature
  230. testCacheModeWithTwoViews
  231. getHtmlPath
  232. getHtmlUrl
  233. getManifestPath
  234. waitUntilHtmlIsRequested
  235. waitUntilManifestIsRequested
  236. waitUntilResourceIsRequested
  237. Feature
  238. testAppCache
  239. Feature
  240. testAppCacheWithTwoViews
  241. Feature
  242. testUseWideViewportWithTwoViews
  243. Feature
  244. testUseWideViewportWithTwoViewsNoQuirks
  245. useWideViewportLayoutWidthTest
  246. Feature
  247. testUseWideViewportLayoutWidth
  248. Feature
  249. testUseWideViewportLayoutWidthNoQuirks
  250. Feature
  251. testUseWideViewportControlsDoubleTabToZoom
  252. Feature
  253. testLoadWithOverviewModeWithTwoViews
  254. Feature
  255. testLoadWithOverviewModeViewportTagWithTwoViews
  256. Feature
  257. testSetInitialScale
  258. runVideoTest
  259. testMediaPlaybackWithoutUserGesture
  260. Feature
  261. testMediaPlaybackWithUserGesture
  262. Feature
  263. testDefaultVideoPosterURL
  264. getContainer0
  265. getContents0
  266. getClient0
  267. getContainer1
  268. getContents1
  269. getClient1
  270. runPerViewSettingsTest
  271. createViews
  272. createViews
  273. assertFileIsReadable
  274. ensureResourceRequestCountInContentProvider
  275. resetResourceRequestCountInContentProvider
  276. createContentUrl
  277. simulateDoubleTapCenterOfWebViewOnUiThread

// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.android_webview.test;

import android.content.Context;
import android.graphics.Point;
import android.os.Build;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.WindowManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;

import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.AwSettings;
import org.chromium.android_webview.AwSettings.LayoutAlgorithm;
import org.chromium.android_webview.InterceptedRequestData;
import org.chromium.android_webview.test.util.CommonResources;
import org.chromium.android_webview.test.util.ImagePageGenerator;
import org.chromium.android_webview.test.util.JavascriptEventObserver;
import org.chromium.android_webview.test.util.VideoTestWebServer;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.TestFileUtil;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.content.browser.ContentViewCore;
import org.chromium.content.browser.test.util.CallbackHelper;
import org.chromium.content.browser.test.util.HistoryUtils;
import org.chromium.net.test.util.TestWebServer;
import org.chromium.ui.gfx.DeviceDisplayInfo;

import java.io.File;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A test suite for AwSettings class. The key objective is to verify that each
 * settings applies either to each individual view or to all views of the
 * application
 */
public class AwSettingsTest extends AwTestBase {
    private static final boolean ENABLED = true;
    private static final boolean DISABLED = false;

    /**
     * A helper class for testing a particular preference from AwSettings.
     * The generic type T is the type of the setting. Usually, to test an
     * effect of the preference, JS code is executed that sets document's title.
     * In this case, requiresJsEnabled constructor argument must be set to true.
     */
    abstract class AwSettingsTestHelper<T> {
        protected final AwContents mAwContents;
        protected final Context mContext;
        protected final TestAwContentsClient mContentViewClient;
        protected final AwSettings mAwSettings;

        AwSettingsTestHelper(AwTestContainerView containerView,
                             TestAwContentsClient contentViewClient,
                             boolean requiresJsEnabled) throws Throwable {
            mAwContents = containerView.getAwContents();
            mContext = containerView.getContext();
            mContentViewClient = contentViewClient;
            mAwSettings = AwSettingsTest.this.getAwSettingsOnUiThread(mAwContents);
            if (requiresJsEnabled) {
                mAwSettings.setJavaScriptEnabled(true);
            }
        }

        void ensureSettingHasAlteredValue() throws Throwable {
            ensureSettingHasValue(getAlteredValue());
        }

        void ensureSettingHasInitialValue() throws Throwable {
            ensureSettingHasValue(getInitialValue());
        }

        void setAlteredSettingValue() throws Throwable {
            setCurrentValue(getAlteredValue());
        }

        void setInitialSettingValue() throws Throwable {
            setCurrentValue(getInitialValue());
        }

        protected abstract T getAlteredValue();

        protected abstract T getInitialValue();

        protected abstract T getCurrentValue();

        protected abstract void setCurrentValue(T value) throws Throwable;

        protected abstract void doEnsureSettingHasValue(T value) throws Throwable;

        protected String getTitleOnUiThread() throws Exception {
            return AwSettingsTest.this.getTitleOnUiThread(mAwContents);
        }

        protected void loadDataSync(String data) throws Throwable {
            AwSettingsTest.this.loadDataSync(
                mAwContents,
                mContentViewClient.getOnPageFinishedHelper(),
                data,
                "text/html",
                false);
        }

        protected void loadUrlSync(String url) throws Throwable {
            AwSettingsTest.this.loadUrlSync(
                mAwContents,
                mContentViewClient.getOnPageFinishedHelper(),
                url);
        }

        protected void loadUrlSyncAndExpectError(String url) throws Throwable {
            AwSettingsTest.this.loadUrlSyncAndExpectError(
                mAwContents,
                mContentViewClient.getOnPageFinishedHelper(),
                mContentViewClient.getOnReceivedErrorHelper(),
                url);
        }

        protected String executeJavaScriptAndWaitForResult(String script) throws Exception {
            return AwSettingsTest.this.executeJavaScriptAndWaitForResult(
                    mAwContents, mContentViewClient, script);
        }

        private void ensureSettingHasValue(T value) throws Throwable {
            assertEquals(value, getCurrentValue());
            doEnsureSettingHasValue(value);
        }
    }

    class AwSettingsJavaScriptTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String JS_ENABLED_STRING = "JS Enabled";
        private static final String JS_DISABLED_STRING = "JS Disabled";

        AwSettingsJavaScriptTestHelper(AwTestContainerView containerView,
                                       TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, false);
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getJavaScriptEnabled();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setJavaScriptEnabled(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadDataSync(getData());
            assertEquals(
                value == ENABLED ? JS_ENABLED_STRING : JS_DISABLED_STRING,
                getTitleOnUiThread());
        }

        private String getData() {
            return "<html><head><title>" + JS_DISABLED_STRING + "</title>"
                    + "</head><body onload=\"document.title='" + JS_ENABLED_STRING
                    + "';\"></body></html>";
        }
    }

    // In contrast to AwSettingsJavaScriptTestHelper, doesn't reload the page when testing
    // JavaScript state.
    class AwSettingsJavaScriptDynamicTestHelper extends AwSettingsJavaScriptTestHelper {
        AwSettingsJavaScriptDynamicTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient);
            // Load the page.
            super.doEnsureSettingHasValue(getInitialValue());
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            String oldTitle = getTitleOnUiThread();
            String newTitle = oldTitle + "_modified";
            executeJavaScriptAndWaitForResult(getScript(newTitle));
            assertEquals(value == ENABLED ? newTitle : oldTitle, getTitleOnUiThread());
        }

        private String getScript(String title) {
            return "document.title='" + title + "';";
        }
    }

    class AwSettingsPluginsTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String PLUGINS_ENABLED_STRING = "Embed";
        private static final String PLUGINS_DISABLED_STRING = "NoEmbed";

        AwSettingsPluginsTestHelper(AwTestContainerView containerView,
                                    TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getPluginsEnabled();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setPluginsEnabled(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadDataSync(getData());
            assertEquals(
                value == ENABLED ? PLUGINS_ENABLED_STRING : PLUGINS_DISABLED_STRING,
                getTitleOnUiThread());
        }

        private String getData() {
            return "<html><body onload=\"document.title = document.body.innerText;\">"
                    + "<noembed>No</noembed><span>Embed</span></body></html>";
        }
    }

    class AwSettingsStandardFontFamilyTestHelper extends AwSettingsTestHelper<String> {
        AwSettingsStandardFontFamilyTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
        }

        @Override
        protected String getAlteredValue() {
            return "cursive";
        }

        @Override
        protected String getInitialValue() {
            return "sans-serif";
        }

        @Override
        protected String getCurrentValue() {
            return mAwSettings.getStandardFontFamily();
        }

        @Override
        protected void setCurrentValue(String value) {
            mAwSettings.setStandardFontFamily(value);
        }

        @Override
        protected void doEnsureSettingHasValue(String value) throws Throwable {
            loadDataSync(getData());
            assertEquals(value, getTitleOnUiThread());
        }

        private String getData() {
            return "<html><body onload=\"document.title = " +
                    "getComputedStyle(document.body).getPropertyValue('font-family');\">"
                    + "</body></html>";
        }
    }

    class AwSettingsDefaultFontSizeTestHelper extends AwSettingsTestHelper<Integer> {
        AwSettingsDefaultFontSizeTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
        }

        @Override
        protected Integer getAlteredValue() {
            return 42;
        }

        @Override
        protected Integer getInitialValue() {
            return 16;
        }

        @Override
        protected Integer getCurrentValue() {
            return mAwSettings.getDefaultFontSize();
        }

        @Override
        protected void setCurrentValue(Integer value) {
            mAwSettings.setDefaultFontSize(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Integer value) throws Throwable {
            loadDataSync(getData());
            assertEquals(value.toString() + "px", getTitleOnUiThread());
        }

        private String getData() {
            return "<html><body onload=\"document.title = " +
                    "getComputedStyle(document.body).getPropertyValue('font-size');\">"
                    + "</body></html>";
        }
    }

    class AwSettingsLoadImagesAutomaticallyTestHelper extends AwSettingsTestHelper<Boolean> {
        private ImagePageGenerator mGenerator;

        AwSettingsLoadImagesAutomaticallyTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient,
                ImagePageGenerator generator) throws Throwable {
            super(containerView, contentViewClient, true);
            mGenerator = generator;
        }

        @Override
        protected Boolean getAlteredValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getLoadsImagesAutomatically();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setLoadsImagesAutomatically(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadDataSync(mGenerator.getPageSource());
            assertEquals(value == ENABLED ?
                         ImagePageGenerator.IMAGE_LOADED_STRING :
                         ImagePageGenerator.IMAGE_NOT_LOADED_STRING,
                         getTitleOnUiThread());
        }
    }


    class AwSettingsImagesEnabledHelper extends AwSettingsTestHelper<Boolean> {

        AwSettingsImagesEnabledHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient,
                TestWebServer webServer,
                ImagePageGenerator generator) throws Throwable {
            super(containerView, contentViewClient, true);
            mWebServer = webServer;
            mGenerator = generator;
        }

        @Override
        protected Boolean getAlteredValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getImagesEnabled();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setImagesEnabled(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            final String httpImageUrl = mGenerator.getPageUrl(mWebServer);
            AwSettingsTest.this.loadUrlSync(
                    mAwContents,
                    mContentViewClient.getOnPageFinishedHelper(),
                    httpImageUrl);
            assertEquals(value == ENABLED ?
                         ImagePageGenerator.IMAGE_LOADED_STRING :
                         ImagePageGenerator.IMAGE_NOT_LOADED_STRING,
                         getTitleOnUiThread());
        }

        private TestWebServer mWebServer;
        private ImagePageGenerator mGenerator;
    }

    class AwSettingsDefaultTextEncodingTestHelper extends AwSettingsTestHelper<String> {
        AwSettingsDefaultTextEncodingTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
        }

        @Override
        protected String getAlteredValue() {
            return "utf-8";
        }

        @Override
        protected String getInitialValue() {
            return "Latin-1";
        }

        @Override
        protected String getCurrentValue() {
            return mAwSettings.getDefaultTextEncodingName();
        }

        @Override
        protected void setCurrentValue(String value) {
            mAwSettings.setDefaultTextEncodingName(value);
        }

        @Override
        protected void doEnsureSettingHasValue(String value) throws Throwable {
            loadDataSync(getData());
            assertEquals(value, getTitleOnUiThread());
        }

        private String getData() {
            return "<html><body onload='document.title=document.defaultCharset'></body></html>";
        }
    }

    class AwSettingsUserAgentStringTestHelper extends AwSettingsTestHelper<String> {
        private final String mDefaultUa;
        private static final String DEFAULT_UA = "";
        private static final String CUSTOM_UA = "ChromeViewTest";

        AwSettingsUserAgentStringTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
            mDefaultUa = mAwSettings.getUserAgentString();
        }

        @Override
        protected String getAlteredValue() {
            return CUSTOM_UA;
        }

        @Override
        protected String getInitialValue() {
            return DEFAULT_UA;
        }

        @Override
        protected String getCurrentValue() {
            // The test framework expects that getXXX() == Z after setXXX(Z).
            // But setUserAgentString("" / null) resets the UA string to default,
            // and getUserAgentString returns the default UA string afterwards.
            // To align with the framework, we return an empty string instead of
            // the default UA.
            String currentUa = mAwSettings.getUserAgentString();
            return mDefaultUa.equals(currentUa) ? DEFAULT_UA : currentUa;
        }

        @Override
        protected void setCurrentValue(String value) {
            mAwSettings.setUserAgentString(value);
        }

        @Override
        protected void doEnsureSettingHasValue(String value) throws Throwable {
            loadDataSync(getData());
            assertEquals(
                DEFAULT_UA.equals(value) ? mDefaultUa : value,
                getTitleOnUiThread());
        }

        private String getData() {
            return "<html><body onload='document.title=navigator.userAgent'></body></html>";
        }
    }

    class AwSettingsDomStorageEnabledTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String TEST_FILE = "webview/localStorage.html";
        private static final String NO_LOCAL_STORAGE = "No localStorage";
        private static final String HAS_LOCAL_STORAGE = "Has localStorage";

        AwSettingsDomStorageEnabledTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getDomStorageEnabled();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setDomStorageEnabled(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            // It is not permitted to access localStorage from data URLs in WebKit,
            // that is why a standalone page must be used.
            loadUrlSync(UrlUtils.getTestFileUrl(TEST_FILE));
            assertEquals(
                value == ENABLED ? HAS_LOCAL_STORAGE : NO_LOCAL_STORAGE,
                getTitleOnUiThread());
        }
    }

    class AwSettingsDatabaseTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String TEST_FILE = "webview/database_access.html";
        private static final String NO_DATABASE = "No database";
        private static final String HAS_DATABASE = "Has database";

        AwSettingsDatabaseTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getDatabaseEnabled();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setDatabaseEnabled(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            // It seems accessing the database through a data scheme is not
            // supported, and fails with a DOM exception (likely a cross-domain
            // violation).
            loadUrlSync(UrlUtils.getTestFileUrl(TEST_FILE));
            assertEquals(
                value == ENABLED ? HAS_DATABASE : NO_DATABASE,
                getTitleOnUiThread());
        }
    }

    class AwSettingsUniversalAccessFromFilesTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String TEST_CONTAINER_FILE = "webview/iframe_access.html";
        private static final String TEST_FILE = "webview/hello_world.html";
        private static final String ACCESS_DENIED_TITLE = "Exception";

        AwSettingsUniversalAccessFromFilesTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_CONTAINER_FILE));
            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
            mIframeContainerUrl = UrlUtils.getTestFileUrl(TEST_CONTAINER_FILE);
            mIframeUrl = UrlUtils.getTestFileUrl(TEST_FILE);
            // The value of the setting depends on the SDK version.
            mAwSettings.setAllowUniversalAccessFromFileURLs(false);
            // If universal access is true, the value of file access doesn't
            // matter. While if universal access is false, having file access
            // enabled will allow file loading.
            mAwSettings.setAllowFileAccessFromFileURLs(false);
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getAllowUniversalAccessFromFileURLs();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setAllowUniversalAccessFromFileURLs(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadUrlSync(mIframeContainerUrl);
            assertEquals(
                value == ENABLED ? mIframeUrl : ACCESS_DENIED_TITLE,
                getTitleOnUiThread());
        }

        private final String mIframeContainerUrl;
        private final String mIframeUrl;
    }

    class AwSettingsFileAccessFromFilesIframeTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String TEST_CONTAINER_FILE = "webview/iframe_access.html";
        private static final String TEST_FILE = "webview/hello_world.html";
        private static final String ACCESS_DENIED_TITLE = "Exception";

        AwSettingsFileAccessFromFilesIframeTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_CONTAINER_FILE));
            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
            mIframeContainerUrl = UrlUtils.getTestFileUrl(TEST_CONTAINER_FILE);
            mIframeUrl = UrlUtils.getTestFileUrl(TEST_FILE);
            mAwSettings.setAllowUniversalAccessFromFileURLs(false);
            // The value of the setting depends on the SDK version.
            mAwSettings.setAllowFileAccessFromFileURLs(false);
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getAllowFileAccessFromFileURLs();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setAllowFileAccessFromFileURLs(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadUrlSync(mIframeContainerUrl);
            assertEquals(
                value == ENABLED ? mIframeUrl : ACCESS_DENIED_TITLE,
                getTitleOnUiThread());
        }

        private final String mIframeContainerUrl;
        private final String mIframeUrl;
    }

    class AwSettingsFileAccessFromFilesXhrTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String TEST_FILE = "webview/xhr_access.html";
        private static final String ACCESS_GRANTED_TITLE = "Hello, World!";
        private static final String ACCESS_DENIED_TITLE = "Exception";

        AwSettingsFileAccessFromFilesXhrTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
            assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
            mXhrContainerUrl = UrlUtils.getTestFileUrl(TEST_FILE);
            mAwSettings.setAllowUniversalAccessFromFileURLs(false);
            // The value of the setting depends on the SDK version.
            mAwSettings.setAllowFileAccessFromFileURLs(false);
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getAllowFileAccessFromFileURLs();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setAllowFileAccessFromFileURLs(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadUrlSync(mXhrContainerUrl);
            assertEquals(
                value == ENABLED ? ACCESS_GRANTED_TITLE : ACCESS_DENIED_TITLE,
                getTitleOnUiThread());
        }

        private final String mXhrContainerUrl;
    }

    class AwSettingsFileUrlAccessTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String TEST_FILE = "webview/hello_world.html";
        private static final String ACCESS_GRANTED_TITLE = "Hello, World!";

        AwSettingsFileUrlAccessTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient,
                int startIndex) throws Throwable {
            super(containerView, contentViewClient, true);
            mIndex = startIndex;
            AwSettingsTest.assertFileIsReadable(UrlUtils.getTestFilePath(TEST_FILE));
        }

        @Override
        protected Boolean getAlteredValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getAllowFileAccess();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setAllowFileAccess(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            // Use query parameters to avoid hitting a cached page.
            String fileUrl = UrlUtils.getTestFileUrl(TEST_FILE + "?id=" + mIndex);
            mIndex += 2;
            if (value == ENABLED) {
                loadUrlSync(fileUrl);
                assertEquals(ACCESS_GRANTED_TITLE, getTitleOnUiThread());
            } else {
                loadUrlSyncAndExpectError(fileUrl);
            }
        }

        private int mIndex;
    }

    class AwSettingsContentUrlAccessTestHelper extends AwSettingsTestHelper<Boolean> {

        AwSettingsContentUrlAccessTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient,
                int index) throws Throwable {
            super(containerView, contentViewClient, true);
            mTarget = "content_access_" + index;
        }

        @Override
        protected Boolean getAlteredValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getAllowContentAccess();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setAllowContentAccess(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            AwSettingsTest.this.resetResourceRequestCountInContentProvider(mTarget);
            loadUrlSync(AwSettingsTest.this.createContentUrl(mTarget));
            if (value == ENABLED) {
                AwSettingsTest.this.ensureResourceRequestCountInContentProvider(mTarget, 1);
            } else {
                AwSettingsTest.this.ensureResourceRequestCountInContentProvider(mTarget, 0);
            }
        }

        private final String mTarget;
    }

    class AwSettingsContentUrlAccessFromFileTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String TARGET = "content_from_file";

        AwSettingsContentUrlAccessFromFileTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient,
                int index) throws Throwable {
            super(containerView, contentViewClient, true);
            mIndex = index;
            mTempDir = getInstrumentation().getTargetContext().getCacheDir().getPath();
        }

        @Override
        protected Boolean getAlteredValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getAllowContentAccess();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setAllowContentAccess(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            AwSettingsTest.this.resetResourceRequestCountInContentProvider(TARGET);
            final String fileName = mTempDir + "/" + TARGET + ".html";
            try {
                TestFileUtil.createNewHtmlFile(fileName,
                        TARGET,
                        "<img src=\"" +
                        // Adding a query avoids hitting a cached image, and also verifies
                        // that content URL query parameters are ignored when accessing
                        // a content provider.
                        AwSettingsTest.this.createContentUrl(TARGET + "?id=" + mIndex) + "\">");
                mIndex += 2;
                loadUrlSync("file://" + fileName);
                if (value == ENABLED) {
                    AwSettingsTest.this.ensureResourceRequestCountInContentProvider(TARGET, 1);
                } else {
                    AwSettingsTest.this.ensureResourceRequestCountInContentProvider(TARGET, 0);
                }
            } finally {
                TestFileUtil.deleteFile(fileName);
            }
        }

        private int mIndex;
        private String mTempDir;
    }

    // This class provides helper methods for testing of settings related to
    // the text autosizing feature.
    abstract class AwSettingsTextAutosizingTestHelper<T> extends AwSettingsTestHelper<T> {
        protected static final float PARAGRAPH_FONT_SIZE = 14.0f;

        AwSettingsTextAutosizingTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
            mNeedToWaitForFontSizeChange = false;
            loadDataSync(getData());
        }

        @Override
        protected void setCurrentValue(T value) throws Throwable {
            mNeedToWaitForFontSizeChange = false;
            if (value != getCurrentValue()) {
                mOldFontSize = getActualFontSize();
                mNeedToWaitForFontSizeChange = true;
            }
        }

        protected float getActualFontSize() throws Throwable {
            if (!mNeedToWaitForFontSizeChange) {
                executeJavaScriptAndWaitForResult("setTitleToActualFontSize()");
            } else {
                final float oldFontSize = mOldFontSize;
                poll(new Callable<Boolean>() {
                    @Override
                    public Boolean call() throws Exception {
                        executeJavaScriptAndWaitForResult("setTitleToActualFontSize()");
                        float newFontSize = Float.parseFloat(getTitleOnUiThread());
                        return newFontSize != oldFontSize;
                    }
                });
                mNeedToWaitForFontSizeChange = false;
            }
            return Float.parseFloat(getTitleOnUiThread());
        }

        protected String getData() {
            DeviceDisplayInfo deviceInfo = DeviceDisplayInfo.create(mContext);
            int displayWidth = (int) (deviceInfo.getDisplayWidth() / deviceInfo.getDIPScale());
            int layoutWidth = (int) (displayWidth * 2.5f); // Use 2.5 as autosizing layout tests do.
            StringBuilder sb = new StringBuilder();
            sb.append("<html>" +
                    "<head>" +
                    "<meta name=\"viewport\" content=\"width=" + layoutWidth + "\">" +
                    "<style>" +
                    "body { width: " + layoutWidth + "px; margin: 0; overflow-y: hidden; }" +
                    "</style>" +
                    "<script>" +
                    "function setTitleToActualFontSize() {" +
                    // parseFloat is used to trim out the "px" suffix.
                    "  document.title = parseFloat(getComputedStyle(" +
                    "    document.getElementById('par')).getPropertyValue('font-size'));" +
                    "}</script></head>" +
                    "<body>" +
                    "<p id=\"par\" style=\"font-size:");
            sb.append(PARAGRAPH_FONT_SIZE);
            sb.append("px;\">");
            // Make the paragraph wide enough for being processed by the font autosizer.
            for (int i = 0; i < 500; i++) {
                sb.append("Hello, World! ");
            }
            sb.append("</p></body></html>");
            return sb.toString();
        }

        private boolean mNeedToWaitForFontSizeChange;
        private float mOldFontSize;
    }

    class AwSettingsLayoutAlgorithmTestHelper extends
                                              AwSettingsTextAutosizingTestHelper<LayoutAlgorithm> {

        AwSettingsLayoutAlgorithmTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient);
            // Font autosizing doesn't step in for narrow layout widths.
            mAwSettings.setUseWideViewPort(true);
        }

        @Override
        protected LayoutAlgorithm getAlteredValue() {
            return LayoutAlgorithm.TEXT_AUTOSIZING;
        }

        @Override
        protected LayoutAlgorithm getInitialValue() {
            return LayoutAlgorithm.NARROW_COLUMNS;
        }

        @Override
        protected LayoutAlgorithm getCurrentValue() {
            return mAwSettings.getLayoutAlgorithm();
        }

        @Override
        protected void setCurrentValue(LayoutAlgorithm value) throws Throwable {
            super.setCurrentValue(value);
            mAwSettings.setLayoutAlgorithm(value);
        }

        @Override
        protected void doEnsureSettingHasValue(LayoutAlgorithm value) throws Throwable {
            final float actualFontSize = getActualFontSize();
            if (value == LayoutAlgorithm.TEXT_AUTOSIZING) {
                assertFalse("Actual font size: " + actualFontSize,
                        actualFontSize == PARAGRAPH_FONT_SIZE);
            } else {
                assertTrue("Actual font size: " + actualFontSize,
                        actualFontSize == PARAGRAPH_FONT_SIZE);
            }
        }
    }

    class AwSettingsTextZoomTestHelper extends AwSettingsTextAutosizingTestHelper<Integer> {
        private static final int INITIAL_TEXT_ZOOM = 100;
        private final float mInitialActualFontSize;

        AwSettingsTextZoomTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient);
            mInitialActualFontSize = getActualFontSize();
        }

        @Override
        protected Integer getAlteredValue() {
            return INITIAL_TEXT_ZOOM * 2;
        }

        @Override
        protected Integer getInitialValue() {
            return INITIAL_TEXT_ZOOM;
        }

        @Override
        protected Integer getCurrentValue() {
            return mAwSettings.getTextZoom();
        }

        @Override
        protected void setCurrentValue(Integer value) throws Throwable {
            super.setCurrentValue(value);
            mAwSettings.setTextZoom(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Integer value) throws Throwable {
            final float actualFontSize = getActualFontSize();
            // Ensure that actual vs. initial font size ratio is similar to actual vs. initial
            // text zoom values ratio.
            final float ratiosDelta = Math.abs(
                (actualFontSize / mInitialActualFontSize) -
                (value / (float) INITIAL_TEXT_ZOOM));
            assertTrue(
                "|(" + actualFontSize + " / " + mInitialActualFontSize + ") - (" +
                value + " / " + INITIAL_TEXT_ZOOM + ")| = " + ratiosDelta,
                ratiosDelta <= 0.2f);
        }
    }

    class AwSettingsTextZoomAutosizingTestHelper
            extends AwSettingsTextAutosizingTestHelper<Integer> {
        private static final int INITIAL_TEXT_ZOOM = 100;
        private final float mInitialActualFontSize;

        AwSettingsTextZoomAutosizingTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient);
            mAwSettings.setLayoutAlgorithm(LayoutAlgorithm.TEXT_AUTOSIZING);
            // The initial font size can be adjusted by font autosizer depending on the page's
            // viewport width.
            mInitialActualFontSize = getActualFontSize();
        }

        @Override
        protected Integer getAlteredValue() {
            return INITIAL_TEXT_ZOOM * 2;
        }

        @Override
        protected Integer getInitialValue() {
            return INITIAL_TEXT_ZOOM;
        }

        @Override
        protected Integer getCurrentValue() {
            return mAwSettings.getTextZoom();
        }

        @Override
        protected void setCurrentValue(Integer value) throws Throwable {
            super.setCurrentValue(value);
            mAwSettings.setTextZoom(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Integer value) throws Throwable {
            final float actualFontSize = getActualFontSize();
            // Ensure that actual vs. initial font size ratio is similar to actual vs. initial
            // text zoom values ratio.
            final float ratiosDelta = Math.abs(
                (actualFontSize / mInitialActualFontSize) -
                (value / (float) INITIAL_TEXT_ZOOM));
            assertTrue(
                "|(" + actualFontSize + " / " + mInitialActualFontSize + ") - (" +
                value + " / " + INITIAL_TEXT_ZOOM + ")| = " + ratiosDelta,
                ratiosDelta <= 0.2f);
        }
    }

    class AwSettingsJavaScriptPopupsTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String POPUP_ENABLED = "Popup enabled";
        private static final String POPUP_BLOCKED = "Popup blocked";

        AwSettingsJavaScriptPopupsTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getJavaScriptCanOpenWindowsAutomatically();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setJavaScriptCanOpenWindowsAutomatically(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadDataSync(getData());
            final boolean expectPopupEnabled = value;
            poll(new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    String title = getTitleOnUiThread();
                    return expectPopupEnabled ? POPUP_ENABLED.equals(title) :
                            POPUP_BLOCKED.equals(title);
                }
            });
            assertEquals(value ? POPUP_ENABLED : POPUP_BLOCKED, getTitleOnUiThread());
        }

        private String getData() {
            return "<html><head>" +
                    "<script>" +
                    "    function tryOpenWindow() {" +
                    "        var newWindow = window.open(" +
                    "           'data:text/html;charset=utf-8," +
                    "           <html><head><title>" + POPUP_ENABLED + "</title></head></html>');" +
                    "        if (!newWindow) document.title = '" + POPUP_BLOCKED + "';" +
                    "    }" +
                    "</script></head>" +
                    "<body onload='tryOpenWindow()'></body></html>";
        }
    }

    class AwSettingsCacheModeTestHelper extends AwSettingsTestHelper<Integer> {

        AwSettingsCacheModeTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient,
                int index,
                TestWebServer webServer) throws Throwable {
            super(containerView, contentViewClient, true);
            mIndex = index;
            mWebServer = webServer;
        }

        @Override
        protected Integer getAlteredValue() {
            // We use the value that results in a behaviour completely opposite to default.
            return WebSettings.LOAD_CACHE_ONLY;
        }

        @Override
        protected Integer getInitialValue() {
            return WebSettings.LOAD_DEFAULT;
        }

        @Override
        protected Integer getCurrentValue() {
            return mAwSettings.getCacheMode();
        }

        @Override
        protected void setCurrentValue(Integer value) {
            mAwSettings.setCacheMode(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Integer value) throws Throwable {
            final String htmlPath = "/cache_mode_" + mIndex + ".html";
            mIndex += 2;
            final String url = mWebServer.setResponse(htmlPath, "response", null);
            assertEquals(0, mWebServer.getRequestCount(htmlPath));
            if (value == WebSettings.LOAD_DEFAULT) {
                loadUrlSync(url);
                assertEquals(1, mWebServer.getRequestCount(htmlPath));
            } else {
                loadUrlSyncAndExpectError(url);
                assertEquals(0, mWebServer.getRequestCount(htmlPath));
            }
        }

        private int mIndex;
        private TestWebServer mWebServer;
    }

    // To verify whether UseWideViewport works, we check, if the page width specified
    // in the "meta viewport" tag is applied. When UseWideViewport is turned off, the
    // "viewport" tag is ignored, and the layout width is set to device width in DIP pixels.
    // We specify a very high width value to make sure that it doesn't intersect with
    // device screen widths (in DIP pixels).
    class AwSettingsUseWideViewportTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final String VIEWPORT_TAG_LAYOUT_WIDTH = "3000";

        AwSettingsUseWideViewportTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient) throws Throwable {
            super(containerView, contentViewClient, true);
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getUseWideViewPort();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mAwSettings.setUseWideViewPort(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadDataSync(getData());
            final String bodyWidth = getTitleOnUiThread();
            if (value) {
                assertTrue(bodyWidth, VIEWPORT_TAG_LAYOUT_WIDTH.equals(bodyWidth));
            } else {
                assertFalse(bodyWidth, VIEWPORT_TAG_LAYOUT_WIDTH.equals(bodyWidth));
            }
        }

        private String getData() {
            return "<html><head>" +
                    "<meta name='viewport' content='width=" + VIEWPORT_TAG_LAYOUT_WIDTH + "' />" +
                    "</head>" +
                    "<body onload='document.title=document.body.clientWidth'></body></html>";
        }
    }

    class AwSettingsLoadWithOverviewModeTestHelper extends AwSettingsTestHelper<Boolean> {
        private static final float DEFAULT_PAGE_SCALE = 1.0f;

        AwSettingsLoadWithOverviewModeTestHelper(
                AwTestContainerView containerView,
                TestAwContentsClient contentViewClient,
                boolean withViewPortTag) throws Throwable {
            super(containerView, contentViewClient, true);
            mWithViewPortTag = withViewPortTag;
            mAwSettings.setUseWideViewPort(true);
        }

        @Override
        protected Boolean getAlteredValue() {
            return ENABLED;
        }

        @Override
        protected Boolean getInitialValue() {
            return DISABLED;
        }

        @Override
        protected Boolean getCurrentValue() {
            return mAwSettings.getLoadWithOverviewMode();
        }

        @Override
        protected void setCurrentValue(Boolean value) {
            mExpectScaleChange = mAwSettings.getLoadWithOverviewMode() != value;
            if (mExpectScaleChange) {
                mOnScaleChangedCallCount =
                        mContentViewClient.getOnScaleChangedHelper().getCallCount();
            }
            mAwSettings.setLoadWithOverviewMode(value);
        }

        @Override
        protected void doEnsureSettingHasValue(Boolean value) throws Throwable {
            loadDataSync(getData());
            if (mExpectScaleChange) {
                mContentViewClient.getOnScaleChangedHelper().
                        waitForCallback(mOnScaleChangedCallCount);
                mExpectScaleChange = false;
            }
            float currentScale = AwSettingsTest.this.getScaleOnUiThread(mAwContents);
            if (value) {
                assertTrue("Expected: " + currentScale + " < " + DEFAULT_PAGE_SCALE,
                        currentScale < DEFAULT_PAGE_SCALE);
            } else {
                assertEquals(DEFAULT_PAGE_SCALE, currentScale);
            }
        }

        private String getData() {
            return "<html><head>" +
                    (mWithViewPortTag ? "<meta name='viewport' content='width=3000' />" : "") +
                    "</head>" +
                    "<body></body></html>";
        }

        private final boolean mWithViewPortTag;
        private boolean mExpectScaleChange;
        private int mOnScaleChangedCallCount;
    }

    // The test verifies that JavaScript is disabled upon WebView
    // creation without accessing AwSettings. If the test passes,
    // it means that WebView-specific web preferences configuration
    // is applied on WebView creation. JS state is used, because it is
    // enabled by default in Chrome, but must be disabled by default
    // in WebView.
    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testJavaScriptDisabledByDefault() throws Throwable {
        final String JS_ENABLED_STRING = "JS has run";
        final String JS_DISABLED_STRING = "JS has not run";
        final String TEST_PAGE_HTML =
                "<html><head><title>" + JS_DISABLED_STRING + "</title>"
                + "</head><body onload=\"document.title='" + JS_ENABLED_STRING
                + "';\"></body></html>";
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        loadDataSync(
            awContents,
            contentClient.getOnPageFinishedHelper(),
            TEST_PAGE_HTML,
            "text/html",
            false);
        assertEquals(JS_DISABLED_STRING, getTitleOnUiThread(awContents));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testJavaScriptEnabledWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsJavaScriptTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsJavaScriptTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testJavaScriptEnabledDynamicWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsJavaScriptDynamicTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsJavaScriptDynamicTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testPluginsEnabledWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsPluginsTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsPluginsTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testStandardFontFamilyWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsStandardFontFamilyTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsStandardFontFamilyTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testDefaultFontSizeWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsDefaultFontSizeTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsDefaultFontSizeTestHelper(views.getContainer1(), views.getClient1()));
    }

    // The test verifies that after changing the LoadsImagesAutomatically
    // setting value from false to true previously skipped images are
    // automatically loaded.
    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testLoadsImagesAutomaticallyNoPageReload() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        AwSettings settings = getAwSettingsOnUiThread(awContents);
        settings.setJavaScriptEnabled(true);
        ImagePageGenerator generator = new ImagePageGenerator(0, false);
        settings.setLoadsImagesAutomatically(false);
        loadDataSync(awContents,
                     contentClient.getOnPageFinishedHelper(),
                     generator.getPageSource(),
                     "text/html", false);
        assertEquals(ImagePageGenerator.IMAGE_NOT_LOADED_STRING,
                getTitleOnUiThread(awContents));
        settings.setLoadsImagesAutomatically(true);
        poll(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                return !ImagePageGenerator.IMAGE_NOT_LOADED_STRING.equals(
                        getTitleOnUiThread(awContents));
            }
        });
        assertEquals(ImagePageGenerator.IMAGE_LOADED_STRING, getTitleOnUiThread(awContents));
    }


    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testLoadsImagesAutomaticallyWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsLoadImagesAutomaticallyTestHelper(
                views.getContainer0(), views.getClient0(), new ImagePageGenerator(0, true)),
            new AwSettingsLoadImagesAutomaticallyTestHelper(
                views.getContainer1(), views.getClient1(), new ImagePageGenerator(1, true)));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testDefaultTextEncodingWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsDefaultTextEncodingTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsDefaultTextEncodingTestHelper(views.getContainer1(), views.getClient1()));
    }

    // The test verifies that the default user agent string follows the format
    // defined in Android CTS tests:
    //
    // Mozilla/5.0 (Linux;[ U;] Android <version>;[ <language>-<country>;]
    // [<devicemodel>;] Build/<buildID>) AppleWebKit/<major>.<minor> (KHTML, like Gecko)
    // Version/<major>.<minor>[ Mobile] Safari/<major>.<minor>
    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUserAgentStringDefault() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        AwSettings settings = getAwSettingsOnUiThread(awContents);
        final String actualUserAgentString = settings.getUserAgentString();
        assertEquals(actualUserAgentString, AwSettings.getDefaultUserAgent());
        final String patternString =
                "Mozilla/5\\.0 \\(Linux;( U;)? Android ([^;]+);( (\\w+)-(\\w+);)?" +
                "\\s?(.*)\\sBuild/(.+)\\) AppleWebKit/(\\d+)\\.(\\d+) \\(KHTML, like Gecko\\) " +
                "Version/\\d+\\.\\d Chrome/\\d+\\.\\d+\\.\\d+\\.\\d+" +
                "( Mobile)? Safari/(\\d+)\\.(\\d+)";
        final Pattern userAgentExpr = Pattern.compile(patternString);
        Matcher patternMatcher = userAgentExpr.matcher(actualUserAgentString);
        assertTrue(String.format("User agent string did not match expected pattern. %nExpected " +
                        "pattern:%n%s%nActual:%n%s", patternString, actualUserAgentString),
                        patternMatcher.find());
        // No country-language code token.
        assertEquals(null, patternMatcher.group(3));
        if ("REL".equals(Build.VERSION.CODENAME)) {
            // Model is only added in release builds
            assertEquals(Build.MODEL, patternMatcher.group(6));
            // Release version is valid only in release builds
            assertEquals(Build.VERSION.RELEASE, patternMatcher.group(2));
        }
        assertEquals(Build.ID, patternMatcher.group(7));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUserAgentStringOverride() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        AwSettings settings = getAwSettingsOnUiThread(awContents);
        final String defaultUserAgentString = settings.getUserAgentString();

        // Check that an attempt to reset the default UA string has no effect.
        settings.setUserAgentString(null);
        assertEquals(defaultUserAgentString, settings.getUserAgentString());
        settings.setUserAgentString("");
        assertEquals(defaultUserAgentString, settings.getUserAgentString());

        // Check that we can also set the default value.
        settings.setUserAgentString(defaultUserAgentString);
        assertEquals(defaultUserAgentString, settings.getUserAgentString());

        // Set a custom UA string, verify that it can be reset back to default.
        final String customUserAgentString = "AwSettingsTest";
        settings.setUserAgentString(customUserAgentString);
        assertEquals(customUserAgentString, settings.getUserAgentString());
        settings.setUserAgentString(null);
        assertEquals(defaultUserAgentString, settings.getUserAgentString());
    }

    // Verify that the current UA override setting has a priority over UA
    // overrides in navigation history entries.
    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUserAgentStringOverrideForHistory() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        final ContentViewCore contentView = testContainerView.getContentViewCore();
        CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper();
        AwSettings settings = getAwSettingsOnUiThread(awContents);
        settings.setJavaScriptEnabled(true);
        final String defaultUserAgentString = settings.getUserAgentString();
        final String customUserAgentString = "AwSettingsTest";
        // We are using different page titles to make sure that we are really
        // going back and forward between them.
        final String pageTemplate =
                "<html><head><title>%s</title></head>" +
                "<body onload='document.title+=navigator.userAgent'></body>" +
                "</html>";
        final String page1Title = "Page1";
        final String page2Title = "Page2";
        final String page1 = String.format(pageTemplate, page1Title);
        final String page2 = String.format(pageTemplate, page2Title);
        settings.setUserAgentString(customUserAgentString);
        loadDataSync(
            awContents, onPageFinishedHelper, page1, "text/html", false);
        assertEquals(page1Title + customUserAgentString, getTitleOnUiThread(awContents));
        loadDataSync(
            awContents, onPageFinishedHelper, page2, "text/html", false);
        assertEquals(page2Title + customUserAgentString, getTitleOnUiThread(awContents));
        settings.setUserAgentString(null);
        // Must not cause any changes until the next page loading.
        assertEquals(page2Title + customUserAgentString, getTitleOnUiThread(awContents));
        HistoryUtils.goBackSync(getInstrumentation(), contentView, onPageFinishedHelper);
        assertEquals(page1Title + defaultUserAgentString, getTitleOnUiThread(awContents));
        HistoryUtils.goForwardSync(getInstrumentation(), contentView,
                                   onPageFinishedHelper);
        assertEquals(page2Title + defaultUserAgentString, getTitleOnUiThread(awContents));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUserAgentStringWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsUserAgentStringTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsUserAgentStringTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUserAgentWithTestServer() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        AwContents awContents = testContainerView.getAwContents();
        AwSettings settings = getAwSettingsOnUiThread(awContents);
        final String customUserAgentString =
                "testUserAgentWithTestServerUserAgent";

        TestWebServer webServer = null;
        String fileName = null;
        try {
            webServer = new TestWebServer(false);
            final String httpPath = "/testUserAgentWithTestServer.html";
            final String url = webServer.setResponse(httpPath, "foo", null);

            settings.setUserAgentString(customUserAgentString);
            loadUrlSync(awContents,
                        contentClient.getOnPageFinishedHelper(),
                        url);

            assertEquals(1, webServer.getRequestCount(httpPath));
            HttpRequest request = webServer.getLastRequest(httpPath);
            Header[] matchingHeaders = request.getHeaders("User-Agent");
            assertEquals(1, matchingHeaders.length);

            Header header = matchingHeaders[0];
            assertEquals(customUserAgentString, header.getValue());
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testDomStorageEnabledWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsDomStorageEnabledTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsDomStorageEnabledTestHelper(views.getContainer1(), views.getClient1()));
    }

    // Ideally, these three tests below should be combined into one, or tested using
    // runPerViewSettingsTest. However, it seems the database setting cannot be toggled
    // once set. Filed b/8186497.
    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testDatabaseInitialValue() throws Throwable {
        TestAwContentsClient client = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(client);
        AwSettingsDatabaseTestHelper helper =
                new AwSettingsDatabaseTestHelper(testContainerView, client);
        helper.ensureSettingHasInitialValue();
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testDatabaseEnabled() throws Throwable {
        TestAwContentsClient client = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(client);
        AwSettingsDatabaseTestHelper helper =
                new AwSettingsDatabaseTestHelper(testContainerView, client);
        helper.setAlteredSettingValue();
        helper.ensureSettingHasAlteredValue();
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testDatabaseDisabled() throws Throwable {
        TestAwContentsClient client = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(client);
        AwSettingsDatabaseTestHelper helper =
                new AwSettingsDatabaseTestHelper(testContainerView, client);
        helper.setInitialSettingValue();
        helper.ensureSettingHasInitialValue();
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUniversalAccessFromFilesWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsUniversalAccessFromFilesTestHelper(views.getContainer0(),
                views.getClient0()),
            new AwSettingsUniversalAccessFromFilesTestHelper(views.getContainer1(),
                views.getClient1()));
    }

    // This test verifies that local image resources can be loaded from file:
    // URLs regardless of file access state.
    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testFileAccessFromFilesImage() throws Throwable {
        final String testFile = "webview/image_access.html";
        assertFileIsReadable(UrlUtils.getTestFilePath(testFile));
        final String imageContainerUrl = UrlUtils.getTestFileUrl(testFile);
        final String imageHeight = "16";
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        AwSettings settings = getAwSettingsOnUiThread(awContents);
        settings.setJavaScriptEnabled(true);
        settings.setAllowUniversalAccessFromFileURLs(false);
        settings.setAllowFileAccessFromFileURLs(false);
        loadUrlSync(awContents, contentClient.getOnPageFinishedHelper(), imageContainerUrl);
        assertEquals(imageHeight, getTitleOnUiThread(awContents));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testFileAccessFromFilesIframeWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsFileAccessFromFilesIframeTestHelper(
                views.getContainer0(), views.getClient0()),
            new AwSettingsFileAccessFromFilesIframeTestHelper(
                views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testFileAccessFromFilesXhrWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsFileAccessFromFilesXhrTestHelper(views.getContainer0(),
                views.getClient0()),
            new AwSettingsFileAccessFromFilesXhrTestHelper(views.getContainer1(),
                views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testFileUrlAccessWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsFileUrlAccessTestHelper(views.getContainer0(), views.getClient0(), 0),
            new AwSettingsFileUrlAccessTestHelper(views.getContainer1(), views.getClient1(), 1));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testContentUrlAccessWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsContentUrlAccessTestHelper(views.getContainer0(), views.getClient0(), 0),
            new AwSettingsContentUrlAccessTestHelper(views.getContainer1(), views.getClient1(), 1));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences", "Navigation"})
    public void testBlockingContentUrlsFromDataUrls() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        final String target = "content_from_data";
        final String page = "<html><body>" +
                "<img src=\"" +
                createContentUrl(target) + "\">" +
                "</body></html>";
        resetResourceRequestCountInContentProvider(target);
        loadDataSync(
            awContents,
            contentClient.getOnPageFinishedHelper(),
            page,
            "text/html",
            false);
        ensureResourceRequestCountInContentProvider(target, 0);
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences", "Navigation"})
    public void testContentUrlFromFileWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsContentUrlAccessFromFileTestHelper(
                    views.getContainer0(), views.getClient0(), 0),
            new AwSettingsContentUrlAccessFromFileTestHelper(
                    views.getContainer1(), views.getClient1(), 1));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testBlockNetworkImagesDoesNotBlockDataUrlImage() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        final AwSettings settings = getAwSettingsOnUiThread(awContents);
        ImagePageGenerator generator = new ImagePageGenerator(0, false);

        settings.setJavaScriptEnabled(true);
        settings.setImagesEnabled(false);
        loadDataSync(awContents,
                     contentClient.getOnPageFinishedHelper(),
                     generator.getPageSource(),
                     "text/html",
                     false);
        assertEquals(ImagePageGenerator.IMAGE_LOADED_STRING, getTitleOnUiThread(awContents));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testBlockNetworkImagesBlocksNetworkImageAndReloadInPlace() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        final AwSettings settings = getAwSettingsOnUiThread(awContents);
        settings.setJavaScriptEnabled(true);
        ImagePageGenerator generator = new ImagePageGenerator(0, false);

        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            final String httpImageUrl = generator.getPageUrl(webServer);

            settings.setImagesEnabled(false);
            loadUrlSync(awContents, contentClient.getOnPageFinishedHelper(), httpImageUrl);
            assertEquals(ImagePageGenerator.IMAGE_NOT_LOADED_STRING,
                    getTitleOnUiThread(awContents));

            settings.setImagesEnabled(true);
            poll(new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    return ImagePageGenerator.IMAGE_LOADED_STRING.equals(
                        getTitleOnUiThread(awContents));
                }
            });
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testBlockNetworkImagesWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            runPerViewSettingsTest(
                    new AwSettingsImagesEnabledHelper(
                            views.getContainer0(),
                            views.getClient0(),
                            webServer,
                            new ImagePageGenerator(0, true)),
                    new AwSettingsImagesEnabledHelper(
                            views.getContainer1(),
                            views.getClient1(),
                            webServer,
                            new ImagePageGenerator(1, true)));
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testBlockNetworkLoadsWithHttpResources() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainer =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainer.getAwContents();
        final AwSettings awSettings = getAwSettingsOnUiThread(awContents);
        awSettings.setJavaScriptEnabled(true);
        ImagePageGenerator generator = new ImagePageGenerator(0, false);

        TestWebServer webServer = null;
        String fileName = null;
        try {
            // Set up http image.
            webServer = new TestWebServer(false);
            final String httpPath = "/image.png";
            final String imageUrl = webServer.setResponseBase64(
                    httpPath, generator.getImageSourceNoAdvance(),
                    CommonResources.getImagePngHeaders(true));

            // Set up file html that loads http iframe.
            String pageHtml = "<img src='" + imageUrl + "' " +
                      "onload=\"document.title='img_onload_fired';\" " +
                      "onerror=\"document.title='img_onerror_fired';\" />";
            Context context = getInstrumentation().getTargetContext();
            fileName = context.getCacheDir() + "/block_network_loads_test.html";
            TestFileUtil.deleteFile(fileName);  // Remove leftover file if any.
            TestFileUtil.createNewHtmlFile(fileName, "unset", pageHtml);

            // Actual test. Blocking should trigger onerror handler.
            awSettings.setBlockNetworkLoads(true);
            loadUrlSync(
                awContents,
                contentClient.getOnPageFinishedHelper(),
                "file:///" + fileName);
            assertEquals(0, webServer.getRequestCount(httpPath));
            assertEquals("img_onerror_fired", getTitleOnUiThread(awContents));

            // Unblock should load normally.
            awSettings.setBlockNetworkLoads(false);
            loadUrlSync(
                awContents,
                contentClient.getOnPageFinishedHelper(),
                "file:///" + fileName);
            assertEquals(1, webServer.getRequestCount(httpPath));
            assertEquals("img_onload_fired", getTitleOnUiThread(awContents));
        } finally {
            if (fileName != null) TestFileUtil.deleteFile(fileName);
            if (webServer != null) webServer.shutdown();
        }
    }

    private static class AudioEvent {
        private CallbackHelper mCallback;
        public AudioEvent(CallbackHelper callback) {
            mCallback = callback;
        }

        @JavascriptInterface
        public void onCanPlay() {
            mCallback.notifyCalled();
        }

        @JavascriptInterface
        public void onError() {
            mCallback.notifyCalled();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testBlockNetworkLoadsWithAudio() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainer =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainer.getAwContents();
        final AwSettings awSettings = getAwSettingsOnUiThread(awContents);
        CallbackHelper callback = new CallbackHelper();
        awSettings.setJavaScriptEnabled(true);

        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            final String httpPath = "/audio.mp3";
            // Don't care about the response is correct or not, just want
            // to know whether Url is accessed.
            final String audioUrl = webServer.setResponse(httpPath, "1", null);

            String pageHtml = "<html><body><audio controls src='" + audioUrl + "' " +
                    "oncanplay=\"AudioEvent.onCanPlay();\" " +
                    "onerror=\"AudioEvent.onError();\" /> </body></html>";
            // Actual test. Blocking should trigger onerror handler.
            awSettings.setBlockNetworkLoads(true);
            awContents.addPossiblyUnsafeJavascriptInterface(
                    new AudioEvent(callback), "AudioEvent", null);
            int count = callback.getCallCount();
            loadDataSync(awContents, contentClient.getOnPageFinishedHelper(), pageHtml,
                    "text/html", false);
            callback.waitForCallback(count, 1);
            assertEquals(0, webServer.getRequestCount(httpPath));

            // The below test failed in Nexus Galaxy.
            // See https://code.google.com/p/chromium/issues/detail?id=313463
            // Unblock should load normally.
            /*
            awSettings.setBlockNetworkLoads(false);
            count = callback.getCallCount();
            loadDataSync(awContents, contentClient.getOnPageFinishedHelper(), pageHtml,
                    "text/html", false);
            callback.waitForCallback(count, 1);
            assertTrue(0 != webServer.getRequestCount(httpPath));
            */
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    // Test an assert URL (file:///android_asset/)
    @SmallTest
    @Feature({"AndroidWebView", "Navigation"})
    public void testAssetUrl() throws Throwable {
        // Note: this text needs to be kept in sync with the contents of the html file referenced
        // below.
        final String expectedTitle = "Asset File";
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        loadUrlSync(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    "file:///android_asset/asset_file.html");
        assertEquals(expectedTitle, getTitleOnUiThread(awContents));
    }

    // Test a resource URL (file:///android_res/).
    @SmallTest
    @Feature({"AndroidWebView", "Navigation"})
    public void testResourceUrl() throws Throwable {
        // Note: this text needs to be kept in sync with the contents of the html file referenced
        // below.
        final String expectedTitle = "Resource File";
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        loadUrlSync(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    "file:///android_res/raw/resource_file.html");
        assertEquals(expectedTitle, getTitleOnUiThread(awContents));
    }

    // Test that the file URL access toggle does not affect asset URLs.
    @SmallTest
    @Feature({"AndroidWebView", "Navigation"})
    public void testFileUrlAccessToggleDoesNotBlockAssetUrls() throws Throwable {
        // Note: this text needs to be kept in sync with the contents of the html file referenced
        // below.
        final String expectedTitle = "Asset File";
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        final AwSettings settings = getAwSettingsOnUiThread(awContents);
        settings.setAllowFileAccess(false);
        loadUrlSync(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    "file:///android_asset/asset_file.html");
        assertEquals(expectedTitle, getTitleOnUiThread(awContents));
    }

    // Test that the file URL access toggle does not affect resource URLs.
    @SmallTest
    @Feature({"AndroidWebView", "Navigation"})
    public void testFileUrlAccessToggleDoesNotBlockResourceUrls() throws Throwable {
        // Note: this text needs to be kept in sync with the contents of the html file referenced
        // below.
        final String expectedTitle = "Resource File";
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        final AwSettings settings = getAwSettingsOnUiThread(awContents);
        settings.setAllowFileAccess(false);
        loadUrlSync(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    "file:///android_res/raw/resource_file.html");
        assertEquals(expectedTitle, getTitleOnUiThread(awContents));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testLayoutAlgorithmWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsLayoutAlgorithmTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsLayoutAlgorithmTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testTextZoomWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsTextZoomTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsTextZoomTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testTextZoomAutosizingWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsTextZoomAutosizingTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsTextZoomAutosizingTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testJavaScriptPopupsWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsJavaScriptPopupsTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsJavaScriptPopupsTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testCacheMode() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainer =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainer.getAwContents();
        final AwSettings awSettings = getAwSettingsOnUiThread(testContainer.getAwContents());
        clearCacheOnUiThread(awContents, true);

        assertEquals(WebSettings.LOAD_DEFAULT, awSettings.getCacheMode());
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            final String htmlPath = "/testCacheMode.html";
            final String url = webServer.setResponse(htmlPath, "response", null);
            awSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
            loadUrlSync(awContents, contentClient.getOnPageFinishedHelper(), url);
            assertEquals(1, webServer.getRequestCount(htmlPath));
            loadUrlSync(awContents, contentClient.getOnPageFinishedHelper(), url);
            assertEquals(1, webServer.getRequestCount(htmlPath));

            awSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
            loadUrlSync(awContents, contentClient.getOnPageFinishedHelper(), url);
            assertEquals(2, webServer.getRequestCount(htmlPath));
            loadUrlSync(awContents, contentClient.getOnPageFinishedHelper(), url);
            assertEquals(3, webServer.getRequestCount(htmlPath));

            awSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
            loadUrlSync(awContents, contentClient.getOnPageFinishedHelper(), url);
            assertEquals(3, webServer.getRequestCount(htmlPath));
            loadUrlSync(awContents, contentClient.getOnPageFinishedHelper(), url);
            assertEquals(3, webServer.getRequestCount(htmlPath));

            final String htmlNotInCachePath = "/testCacheMode-not-in-cache.html";
            final String urlNotInCache = webServer.setResponse(htmlNotInCachePath, "", null);
            loadUrlSyncAndExpectError(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    contentClient.getOnReceivedErrorHelper(),
                    urlNotInCache);
            assertEquals(0, webServer.getRequestCount(htmlNotInCachePath));
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    // As our implementation of network loads blocking uses the same net::URLRequest settings, make
    // sure that setting cache mode doesn't accidentally enable network loads.  The reference
    // behaviour is that when network loads are blocked, setting cache mode has no effect.
    public void testCacheModeWithBlockedNetworkLoads() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainer =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainer.getAwContents();
        final AwSettings awSettings = getAwSettingsOnUiThread(testContainer.getAwContents());
        clearCacheOnUiThread(awContents, true);

        assertEquals(WebSettings.LOAD_DEFAULT, awSettings.getCacheMode());
        awSettings.setBlockNetworkLoads(true);
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            final String htmlPath = "/testCacheModeWithBlockedNetworkLoads.html";
            final String url = webServer.setResponse(htmlPath, "response", null);
            loadUrlSyncAndExpectError(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    contentClient.getOnReceivedErrorHelper(),
                    url);
            assertEquals(0, webServer.getRequestCount(htmlPath));

            awSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
            loadUrlSyncAndExpectError(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    contentClient.getOnReceivedErrorHelper(),
                    url);
            assertEquals(0, webServer.getRequestCount(htmlPath));

            awSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
            loadUrlSyncAndExpectError(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    contentClient.getOnReceivedErrorHelper(),
                    url);
            assertEquals(0, webServer.getRequestCount(htmlPath));

            awSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
            loadUrlSyncAndExpectError(awContents,
                    contentClient.getOnPageFinishedHelper(),
                    contentClient.getOnReceivedErrorHelper(),
                    url);
            assertEquals(0, webServer.getRequestCount(htmlPath));
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testCacheModeWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            runPerViewSettingsTest(
                    new AwSettingsCacheModeTestHelper(
                            views.getContainer0(), views.getClient0(), 0, webServer),
                    new AwSettingsCacheModeTestHelper(
                            views.getContainer1(), views.getClient1(), 1, webServer));
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    static class ManifestTestHelper {
        private final TestWebServer mWebServer;
        private final String mHtmlPath;
        private final String mHtmlUrl;
        private final String mManifestPath;

        ManifestTestHelper(
                TestWebServer webServer, String htmlPageName, String manifestName) {
            mWebServer = webServer;
            mHtmlPath = "/" + htmlPageName;
            mHtmlUrl = webServer.setResponse(
                    mHtmlPath, "<html manifest=\"" + manifestName + "\"></html>", null);
            mManifestPath = "/" + manifestName;
            webServer.setResponse(
                    mManifestPath,
                    "CACHE MANIFEST",
                    CommonResources.getContentTypeAndCacheHeaders("text/cache-manifest", false));
        }

        String getHtmlPath() {
            return mHtmlPath;
        }

        String getHtmlUrl() {
            return mHtmlUrl;
        }

        String getManifestPath() {
            return mManifestPath;
        }

        int waitUntilHtmlIsRequested(final int initialRequestCount) throws Exception {
            return waitUntilResourceIsRequested(mHtmlPath, initialRequestCount);
        }

        int waitUntilManifestIsRequested(final int initialRequestCount) throws Exception {
            return waitUntilResourceIsRequested(mManifestPath, initialRequestCount);
        }

        private int waitUntilResourceIsRequested(
                final String path, final int initialRequestCount) throws Exception {
            poll(new Callable<Boolean>() {
                @Override
                public Boolean call() throws Exception {
                    return mWebServer.getRequestCount(path) > initialRequestCount;
                }
            });
            return mWebServer.getRequestCount(path);
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences", "AppCache"})
    public void testAppCache() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainer =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainer.getAwContents();
        final AwSettings settings = getAwSettingsOnUiThread(awContents);
        settings.setJavaScriptEnabled(true);
        // Note that the cache isn't actually enabled until the call to setAppCachePath.
        settings.setAppCacheEnabled(true);

        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            ManifestTestHelper helper = new ManifestTestHelper(
                    webServer, "testAppCache.html", "appcache.manifest");
            loadUrlSync(
                    awContents,
                    contentClient.getOnPageFinishedHelper(),
                    helper.getHtmlUrl());
            helper.waitUntilHtmlIsRequested(0);
            // Unfortunately, there is no other good way of verifying that AppCache is
            // disabled, other than checking that it didn't try to fetch the manifest.
            Thread.sleep(1000);
            assertEquals(0, webServer.getRequestCount(helper.getManifestPath()));
            settings.setAppCachePath("whatever");  // Enables AppCache.
            loadUrlSync(
                    awContents,
                    contentClient.getOnPageFinishedHelper(),
                    helper.getHtmlUrl());
            helper.waitUntilManifestIsRequested(0);
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences", "AppCache"})
    public void testAppCacheWithTwoViews() throws Throwable {
        // We don't use the test helper here, because making sure that AppCache
        // is disabled takes a lot of time, so running through the usual drill
        // will take about 20 seconds.
        ViewPair views = createViews();

        AwSettings settings0 = getAwSettingsOnUiThread(views.getContents0());
        settings0.setJavaScriptEnabled(true);
        settings0.setAppCachePath("whatever");
        settings0.setAppCacheEnabled(true);
        AwSettings settings1 = getAwSettingsOnUiThread(views.getContents1());
        settings1.setJavaScriptEnabled(true);
        // AppCachePath setting is global, no need to set it for the second view.
        settings1.setAppCacheEnabled(true);

        TestWebServer webServer = null;
        try {
            webServer = new TestWebServer(false);
            ManifestTestHelper helper0 = new ManifestTestHelper(
                    webServer, "testAppCache_0.html", "appcache.manifest_0");
            loadUrlSync(
                    views.getContents0(),
                    views.getClient0().getOnPageFinishedHelper(),
                    helper0.getHtmlUrl());
            int manifestRequests0 = helper0.waitUntilManifestIsRequested(0);
            ManifestTestHelper helper1 = new ManifestTestHelper(
                    webServer, "testAppCache_1.html", "appcache.manifest_1");
            loadUrlSync(
                    views.getContents1(),
                    views.getClient1().getOnPageFinishedHelper(),
                    helper1.getHtmlUrl());
            helper1.waitUntilManifestIsRequested(0);
            settings1.setAppCacheEnabled(false);
            loadUrlSync(
                    views.getContents0(),
                    views.getClient0().getOnPageFinishedHelper(),
                    helper0.getHtmlUrl());
            helper0.waitUntilManifestIsRequested(manifestRequests0);
            final int prevManifestRequestCount =
                    webServer.getRequestCount(helper1.getManifestPath());
            int htmlRequests1 = webServer.getRequestCount(helper1.getHtmlPath());
            loadUrlSync(
                    views.getContents1(),
                    views.getClient1().getOnPageFinishedHelper(),
                    helper1.getHtmlUrl());
            helper1.waitUntilHtmlIsRequested(htmlRequests1);
            // Unfortunately, there is no other good way of verifying that AppCache is
            // disabled, other than checking that it didn't try to fetch the manifest.
            Thread.sleep(1000);
            assertEquals(
                    prevManifestRequestCount, webServer.getRequestCount(helper1.getManifestPath()));
        } finally {
            if (webServer != null) webServer.shutdown();
        }
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUseWideViewportWithTwoViews() throws Throwable {
        ViewPair views = createViews(true);
        runPerViewSettingsTest(
            new AwSettingsUseWideViewportTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsUseWideViewportTestHelper(views.getContainer1(), views.getClient1()));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUseWideViewportWithTwoViewsNoQuirks() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
            new AwSettingsUseWideViewportTestHelper(views.getContainer0(), views.getClient0()),
            new AwSettingsUseWideViewportTestHelper(views.getContainer1(), views.getClient1()));
    }

    private void useWideViewportLayoutWidthTest(
            AwTestContainerView testContainer, CallbackHelper onPageFinishedHelper)
            throws Throwable {
        final AwContents awContents = testContainer.getAwContents();
        AwSettings settings = getAwSettingsOnUiThread(awContents);

        final String pageTemplate = "<html><head>%s</head>" +
                "<body onload='document.title=document.body.clientWidth'></body></html>";
        final String pageNoViewport = String.format(pageTemplate, "");
        final String pageViewportDeviceWidth = String.format(
                pageTemplate,
                "<meta name='viewport' content='width=device-width' />");
        final String viewportTagSpecifiedWidth = "3000";
        final String pageViewportSpecifiedWidth = String.format(
                pageTemplate,
                "<meta name='viewport' content='width=" + viewportTagSpecifiedWidth + "' />");

        DeviceDisplayInfo deviceInfo = DeviceDisplayInfo.create(testContainer.getContext());
        int displayWidth = (int) (deviceInfo.getDisplayWidth() / deviceInfo.getDIPScale());

        settings.setJavaScriptEnabled(true);
        assertFalse(settings.getUseWideViewPort());
        // When UseWideViewPort is off, "width" setting of "meta viewport"
        // tags is ignored, and the layout width is set to device width in CSS pixels.
        // Thus, all 3 pages will have the same body width.
        loadDataSync(awContents, onPageFinishedHelper, pageNoViewport, "text/html", false);
        int actualWidth = Integer.parseInt(getTitleOnUiThread(awContents));
        // Avoid rounding errors.
        assertTrue("Expected: " + displayWidth + ", Actual: " + actualWidth,
                Math.abs(displayWidth - actualWidth) <= 1);
        loadDataSync(awContents, onPageFinishedHelper, pageViewportDeviceWidth, "text/html", false);
        actualWidth = Integer.parseInt(getTitleOnUiThread(awContents));
        assertTrue("Expected: " + displayWidth + ", Actual: " + actualWidth,
                Math.abs(displayWidth - actualWidth) <= 1);
        loadDataSync(
                awContents, onPageFinishedHelper, pageViewportSpecifiedWidth, "text/html", false);
        actualWidth = Integer.parseInt(getTitleOnUiThread(awContents));
        assertTrue("Expected: " + displayWidth + ", Actual: " + actualWidth,
                Math.abs(displayWidth - actualWidth) <= 1);

        settings.setUseWideViewPort(true);
        // When UseWideViewPort is on, "meta viewport" tag is used.
        // If there is no viewport tag, or width isn't specified,
        // then layout width is set to max(980, <device-width-in-DIP-pixels>)
        loadDataSync(awContents, onPageFinishedHelper, pageNoViewport, "text/html", false);
        actualWidth = Integer.parseInt(getTitleOnUiThread(awContents));
        assertTrue("Expected: >= 980 , Actual: " + actualWidth, actualWidth >= 980);
        loadDataSync(awContents, onPageFinishedHelper, pageViewportDeviceWidth, "text/html", false);
        actualWidth = Integer.parseInt(getTitleOnUiThread(awContents));
        assertTrue("Expected: " + displayWidth + ", Actual: " + actualWidth,
                Math.abs(displayWidth - actualWidth) <= 1);
        loadDataSync(
                awContents, onPageFinishedHelper, pageViewportSpecifiedWidth, "text/html", false);
        assertEquals(viewportTagSpecifiedWidth, getTitleOnUiThread(awContents));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUseWideViewportLayoutWidth() throws Throwable {
        TestAwContentsClient contentClient = new TestAwContentsClient();
        AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        useWideViewportLayoutWidthTest(testContainerView, contentClient.getOnPageFinishedHelper());
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUseWideViewportLayoutWidthNoQuirks() throws Throwable {
        TestAwContentsClient contentClient = new TestAwContentsClient();
        AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient, false);
        useWideViewportLayoutWidthTest(testContainerView, contentClient.getOnPageFinishedHelper());
    }

    @MediumTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testUseWideViewportControlsDoubleTabToZoom() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper();
        AwSettings settings = getAwSettingsOnUiThread(awContents);
        settings.setBuiltInZoomControls(true);

        DeviceDisplayInfo deviceInfo =
                DeviceDisplayInfo.create(testContainerView.getContext());
        int displayWidth = (int) (deviceInfo.getDisplayWidth() / deviceInfo.getDIPScale());
        int layoutWidth = displayWidth * 2;
        final String page = "<html>" +
                "<head><meta name='viewport' content='width=" + layoutWidth + "'>" +
                "<style> body { width: " + layoutWidth + "px; }</style></head>" +
                "<body>Page Text</body></html>";

        assertFalse(settings.getUseWideViewPort());
        // Without wide viewport the <meta viewport> tag will be ignored by WebView,
        // but it doesn't really matter as we don't expect double tap to change the scale.
        loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false);
        final float initialScale = getScaleOnUiThread(awContents);
        simulateDoubleTapCenterOfWebViewOnUiThread(testContainerView);
        Thread.sleep(1000);
        assertEquals(initialScale, getScaleOnUiThread(awContents));

        settings.setUseWideViewPort(true);
        loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false);
        int onScaleChangedCallCount = contentClient.getOnScaleChangedHelper().getCallCount();
        simulateDoubleTapCenterOfWebViewOnUiThread(testContainerView);
        contentClient.getOnScaleChangedHelper().waitForCallback(onScaleChangedCallCount);
        final float zoomedOutScale = getScaleOnUiThread(awContents);
        assertTrue("zoomedOut: " + zoomedOutScale + ", initial: " + initialScale,
                zoomedOutScale < initialScale);
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testLoadWithOverviewModeWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
                new AwSettingsLoadWithOverviewModeTestHelper(
                        views.getContainer0(), views.getClient0(), false),
                new AwSettingsLoadWithOverviewModeTestHelper(
                        views.getContainer1(), views.getClient1(), false));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testLoadWithOverviewModeViewportTagWithTwoViews() throws Throwable {
        ViewPair views = createViews();
        runPerViewSettingsTest(
                new AwSettingsLoadWithOverviewModeTestHelper(
                        views.getContainer0(), views.getClient0(), true),
                new AwSettingsLoadWithOverviewModeTestHelper(
                        views.getContainer1(), views.getClient1(), true));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testSetInitialScale() throws Throwable {
        final TestAwContentsClient contentClient = new TestAwContentsClient();
        final AwTestContainerView testContainerView =
                createAwTestContainerViewOnMainSync(contentClient);
        final AwContents awContents = testContainerView.getAwContents();
        final AwSettings awSettings = getAwSettingsOnUiThread(awContents);
        CallbackHelper onPageFinishedHelper = contentClient.getOnPageFinishedHelper();

        WindowManager wm = (WindowManager) getInstrumentation().getTargetContext()
                .getSystemService(Context.WINDOW_SERVICE);
        Point screenSize = new Point();
        wm.getDefaultDisplay().getSize(screenSize);
        // Make sure after 50% scale, page width still larger than screen.
        int height = screenSize.y * 2 + 1;
        int width = screenSize.x * 2 + 1;
        final String page = "<html><body>" +
                "<p style='height:" + height + "px;width:" + width + "px'>" +
                "testSetInitialScale</p></body></html>";
        final float defaultScale =
            getInstrumentation().getTargetContext().getResources().getDisplayMetrics().density;

        assertEquals(defaultScale, getPixelScaleOnUiThread(awContents), .01f);
        loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false);
        assertEquals(defaultScale, getPixelScaleOnUiThread(awContents), .01f);

        int onScaleChangedCallCount = contentClient.getOnScaleChangedHelper().getCallCount();
        awSettings.setInitialPageScale(50);
        loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false);
        contentClient.getOnScaleChangedHelper().waitForCallback(onScaleChangedCallCount);
        assertEquals(0.5f, getPixelScaleOnUiThread(awContents), .01f);

        onScaleChangedCallCount = contentClient.getOnScaleChangedHelper().getCallCount();
        awSettings.setInitialPageScale(500);
        loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false);
        contentClient.getOnScaleChangedHelper().waitForCallback(onScaleChangedCallCount);
        assertEquals(5.0f, getPixelScaleOnUiThread(awContents), .01f);

        onScaleChangedCallCount = contentClient.getOnScaleChangedHelper().getCallCount();
        awSettings.setInitialPageScale(0);
        loadDataSync(awContents, onPageFinishedHelper, page, "text/html", false);
        contentClient.getOnScaleChangedHelper().waitForCallback(onScaleChangedCallCount);
        assertEquals(defaultScale, getPixelScaleOnUiThread(awContents), .01f);
    }

    /**
     * Run video test.
     * @param requiredUserGesture the settings of MediaPlaybackRequiresUserGesture.
     * @param waitTime time for waiting event happen, -1 means forever.
     * @return true if the event happened,
     * @throws Throwable throw exception if timeout.
     */
    private boolean runVideoTest(final boolean requiredUserGesture, long waitTime)
            throws Throwable {
        final JavascriptEventObserver observer = new JavascriptEventObserver();
        TestAwContentsClient client = new TestAwContentsClient();
        final AwContents awContents = createAwTestContainerViewOnMainSync(client).getAwContents();
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                AwSettings awSettings = awContents.getSettings();
                awSettings.setJavaScriptEnabled(true);
                awSettings.setMediaPlaybackRequiresUserGesture(requiredUserGesture);
                observer.register(awContents.getContentViewCore(), "javaObserver");
            }
        });
        VideoTestWebServer webServer = new VideoTestWebServer(getActivity());
        try {
            String data = "<html><head><script>" +
                "addEventListener('DOMContentLoaded', function() { " +
                "  document.getElementById('video').addEventListener('play', function() { " +
                "    javaObserver.notifyJava(); " +
                "  }, false); " +
                "}, false); " +
                "</script></head><body>" +
                "<video id='video' autoplay control src='" +
                webServer.getOnePixelOneFrameWebmURL() + "' /> </body></html>";
            loadDataAsync(awContents, data, "text/html", false);
            if (waitTime == -1) {
                observer.waitForEvent();
                return true;
            } else {
                return observer.waitForEvent(waitTime);
            }
        } finally {
            if (webServer != null && webServer.getTestWebServer() != null)
                webServer.getTestWebServer().shutdown();
        }
    }

    /*
    @LargeTest
    @Feature({"AndroidWebView", "Preferences"})
    http://crbug.com/304549
    */
    @DisabledTest
    public void testMediaPlaybackWithoutUserGesture() throws Throwable {
        assertTrue(runVideoTest(false, -1));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testMediaPlaybackWithUserGesture() throws Throwable {
        // Wait for 5 second to see if video played.
        assertFalse(runVideoTest(true, 5000));
    }

    @SmallTest
    @Feature({"AndroidWebView", "Preferences"})
    public void testDefaultVideoPosterURL() throws Throwable {
        final CallbackHelper videoPosterAccessedCallbackHelper = new CallbackHelper();
        final String DEFAULT_VIDEO_POSTER_URL = "http://default_video_poster/";
        TestAwContentsClient client = new TestAwContentsClient() {
            @Override
            public InterceptedRequestData shouldInterceptRequest(String url) {
                if (url.equals(DEFAULT_VIDEO_POSTER_URL)) {
                    videoPosterAccessedCallbackHelper.notifyCalled();
                }
                return null;
            }
        };
        final AwContents awContents = createAwTestContainerViewOnMainSync(client).getAwContents();
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                AwSettings awSettings = awContents.getSettings();
                awSettings.setDefaultVideoPosterURL(DEFAULT_VIDEO_POSTER_URL);
            }
        });
        VideoTestWebServer webServer = new VideoTestWebServer(
                getInstrumentation().getTargetContext());
        try {
            String data = "<html><head><body>" +
                "<video id='video' control src='" +
                webServer.getOnePixelOneFrameWebmURL() + "' /> </body></html>";
            loadDataAsync(awContents, data, "text/html", false);
            videoPosterAccessedCallbackHelper.waitForCallback(0, 1, 20, TimeUnit.SECONDS);
        } finally {
            if (webServer.getTestWebServer() != null)
                webServer.getTestWebServer().shutdown();
        }
    }

    static class ViewPair {
        private final AwTestContainerView mContainer0;
        private final TestAwContentsClient mClient0;
        private final AwTestContainerView mContainer1;
        private final TestAwContentsClient mClient1;

        ViewPair(AwTestContainerView container0, TestAwContentsClient client0,
                 AwTestContainerView container1, TestAwContentsClient client1) {
            this.mContainer0 = container0;
            this.mClient0 = client0;
            this.mContainer1 = container1;
            this.mClient1 = client1;
        }

        AwTestContainerView getContainer0() {
            return mContainer0;
        }

        AwContents getContents0() {
            return mContainer0.getAwContents();
        }

        TestAwContentsClient getClient0() {
            return mClient0;
        }

        AwTestContainerView getContainer1() {
            return mContainer1;
        }

        AwContents getContents1() {
            return mContainer1.getAwContents();
        }

        TestAwContentsClient getClient1() {
            return mClient1;
        }
    }

    /**
     * Verifies the following statements about a setting:
     *  - initially, the setting has a default value;
     *  - the setting can be switched to an alternate value and back;
     *  - switching a setting in the first WebView doesn't affect the setting
     *    state in the second WebView and vice versa.
     *
     * @param helper0 Test helper for the first ContentView
     * @param helper1 Test helper for the second ContentView
     * @throws Throwable
     */
    private void runPerViewSettingsTest(AwSettingsTestHelper helper0,
                                        AwSettingsTestHelper helper1) throws Throwable {
        helper0.ensureSettingHasInitialValue();
        helper1.ensureSettingHasInitialValue();

        helper1.setAlteredSettingValue();
        helper0.ensureSettingHasInitialValue();
        helper1.ensureSettingHasAlteredValue();

        helper1.setInitialSettingValue();
        helper0.ensureSettingHasInitialValue();
        helper1.ensureSettingHasInitialValue();

        helper0.setAlteredSettingValue();
        helper0.ensureSettingHasAlteredValue();
        helper1.ensureSettingHasInitialValue();

        helper0.setInitialSettingValue();
        helper0.ensureSettingHasInitialValue();
        helper1.ensureSettingHasInitialValue();

        helper0.setAlteredSettingValue();
        helper0.ensureSettingHasAlteredValue();
        helper1.ensureSettingHasInitialValue();

        helper1.setAlteredSettingValue();
        helper0.ensureSettingHasAlteredValue();
        helper1.ensureSettingHasAlteredValue();

        helper0.setInitialSettingValue();
        helper0.ensureSettingHasInitialValue();
        helper1.ensureSettingHasAlteredValue();

        helper1.setInitialSettingValue();
        helper0.ensureSettingHasInitialValue();
        helper1.ensureSettingHasInitialValue();
    }

    private ViewPair createViews() throws Throwable {
        return createViews(false);
    }

    private ViewPair createViews(boolean supportsLegacyQuirks) throws Throwable {
        TestAwContentsClient client0 = new TestAwContentsClient();
        TestAwContentsClient client1 = new TestAwContentsClient();
        return new ViewPair(
            createAwTestContainerViewOnMainSync(client0, supportsLegacyQuirks),
            client0,
            createAwTestContainerViewOnMainSync(client1, supportsLegacyQuirks),
            client1);
    }

    static void assertFileIsReadable(String filePath) {
        File file = new File(filePath);
        try {
            assertTrue("Test file \"" + filePath + "\" is not readable." +
                    "Please make sure that files from android_webview/test/data/device_files/ " +
                    "has been pushed to the device before testing",
                    file.canRead());
        } catch (SecurityException e) {
            fail("Got a SecurityException for \"" + filePath + "\": " + e.toString());
        }
    }

    /**
     * Verifies the number of resource requests made to the content provider.
     * @param resource Resource name
     * @param expectedCount Expected resource requests count
     */
    private void ensureResourceRequestCountInContentProvider(String resource, int expectedCount) {
        Context context = getInstrumentation().getTargetContext();
        int actualCount = TestContentProvider.getResourceRequestCount(context, resource);
        assertEquals(expectedCount, actualCount);
    }

    private void resetResourceRequestCountInContentProvider(String resource) {
        Context context = getInstrumentation().getTargetContext();
        TestContentProvider.resetResourceRequestCount(context, resource);
    }

    private String createContentUrl(final String target) {
        return TestContentProvider.createContentUrl(target);
    }

    private void simulateDoubleTapCenterOfWebViewOnUiThread(final AwTestContainerView webView)
            throws Throwable {
        final int x = (webView.getRight() - webView.getLeft()) / 2;
        final int y = (webView.getBottom() - webView.getTop()) / 2;
        final AwContents awContents = webView.getAwContents();
        runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                awContents.getContentViewCore().sendDoubleTapForTest(
                        SystemClock.uptimeMillis(), x, y);
            }
        });
    }
}

/* [<][>][^][v][top][bottom][index][help] */