Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit.cts;
     18 
     19 import android.content.ContentResolver;
     20 import android.content.Context;
     21 import android.content.ContextWrapper;
     22 import android.content.res.AssetManager;
     23 import android.graphics.Bitmap;
     24 import android.graphics.Bitmap.Config;
     25 import android.graphics.BitmapFactory;
     26 import android.graphics.Canvas;
     27 import android.graphics.Color;
     28 import android.graphics.Picture;
     29 import android.graphics.Rect;
     30 import android.graphics.pdf.PdfRenderer;
     31 import android.net.Uri;
     32 import android.os.Bundle;
     33 import android.os.CancellationSignal;
     34 import android.os.Handler;
     35 import android.os.LocaleList;
     36 import android.os.Looper;
     37 import android.os.Message;
     38 import android.os.ParcelFileDescriptor;
     39 import android.os.StrictMode;
     40 import android.os.StrictMode.ThreadPolicy;
     41 import android.os.SystemClock;
     42 import android.platform.test.annotations.Presubmit;
     43 import android.print.PageRange;
     44 import android.print.PrintAttributes;
     45 import android.print.PrintDocumentAdapter;
     46 import android.print.PrintDocumentAdapter.LayoutResultCallback;
     47 import android.print.PrintDocumentAdapter.WriteResultCallback;
     48 import android.print.PrintDocumentInfo;
     49 import android.test.ActivityInstrumentationTestCase2;
     50 import android.test.UiThreadTest;
     51 import android.util.AttributeSet;
     52 import android.util.DisplayMetrics;
     53 import android.view.KeyEvent;
     54 import android.view.MotionEvent;
     55 import android.view.View;
     56 import android.view.ViewGroup;
     57 import android.view.textclassifier.TextClassification;
     58 import android.view.textclassifier.TextClassifier;
     59 import android.view.textclassifier.TextSelection;
     60 import android.webkit.ConsoleMessage;
     61 import android.webkit.CookieSyncManager;
     62 import android.webkit.DownloadListener;
     63 import android.webkit.JavascriptInterface;
     64 import android.webkit.SafeBrowsingResponse;
     65 import android.webkit.ValueCallback;
     66 import android.webkit.WebBackForwardList;
     67 import android.webkit.WebChromeClient;
     68 import android.webkit.WebIconDatabase;
     69 import android.webkit.WebResourceRequest;
     70 import android.webkit.WebSettings;
     71 import android.webkit.WebView;
     72 import android.webkit.WebView.HitTestResult;
     73 import android.webkit.WebView.PictureListener;
     74 import android.webkit.WebView.VisualStateCallback;
     75 import android.webkit.WebViewClient;
     76 import android.webkit.WebViewDatabase;
     77 import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
     78 import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
     79 import android.widget.LinearLayout;
     80 
     81 import com.android.compatibility.common.util.EvaluateJsResultPollingCheck;
     82 import com.android.compatibility.common.util.NullWebViewUtils;
     83 import com.android.compatibility.common.util.PollingCheck;
     84 
     85 import junit.framework.Assert;
     86 
     87 import java.io.ByteArrayInputStream;
     88 import java.io.File;
     89 import java.io.FileInputStream;
     90 import java.io.FileNotFoundException;
     91 import java.io.IOException;
     92 
     93 import java.net.MalformedURLException;
     94 import java.net.URL;
     95 
     96 import java.nio.charset.Charset;
     97 import java.nio.charset.StandardCharsets;
     98 
     99 import java.util.Collections;
    100 import java.util.Date;
    101 import java.util.concurrent.atomic.AtomicBoolean;
    102 import java.util.concurrent.atomic.AtomicReference;
    103 import java.util.concurrent.Callable;
    104 import java.util.concurrent.CountDownLatch;
    105 import java.util.concurrent.FutureTask;
    106 import java.util.concurrent.Semaphore;
    107 import java.util.concurrent.TimeUnit;
    108 import java.util.ArrayList;
    109 import java.util.HashMap;
    110 import java.util.List;
    111 import java.util.Map;
    112 
    113 import org.apache.http.Header;
    114 import org.apache.http.HttpEntity;
    115 import org.apache.http.HttpEntityEnclosingRequest;
    116 import org.apache.http.HttpRequest;
    117 import org.apache.http.util.EncodingUtils;
    118 import org.apache.http.util.EntityUtils;
    119 
    120 public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> {
    121     public static final long TEST_TIMEOUT = 20000L;
    122     private static final int INITIAL_PROGRESS = 100;
    123     private static final String X_REQUESTED_WITH = "X-Requested-With";
    124     private static final String PRINTER_TEST_FILE = "print.pdf";
    125     private static final String PDF_PREAMBLE = "%PDF-1";
    126 
    127     /**
    128      * This is the minimum number of milliseconds to wait for scrolling to
    129      * start. If no scrolling has started before this timeout then it is
    130      * assumed that no scrolling will happen.
    131      */
    132     private static final long MIN_SCROLL_WAIT_MS = 1000;
    133 
    134     /**
    135      * This is the minimum number of milliseconds to wait for findAll to
    136      * find all the matches. If matches are not found, the Listener would
    137      * call findAll again until it times out.
    138      */
    139     private static final long MIN_FIND_WAIT_MS = 3000;
    140 
    141     /**
    142      * Once scrolling has started, this is the interval that scrolling
    143      * is checked to see if there is a change. If no scrolling change
    144      * has happened in the given time then it is assumed that scrolling
    145      * has stopped.
    146      */
    147     private static final long SCROLL_WAIT_INTERVAL_MS = 200;
    148 
    149     /**
    150      * Epsilon used in page scale value comparisons.
    151      */
    152     private static final float PAGE_SCALE_EPSILON = 0.0001f;
    153 
    154     private WebView mWebView;
    155     private CtsTestServer mWebServer;
    156     private WebViewOnUiThread mOnUiThread;
    157     private WebIconDatabase mIconDb;
    158 
    159     public WebViewTest() {
    160         super("com.android.cts.webkit", WebViewCtsActivity.class);
    161     }
    162 
    163     @Override
    164     protected void setUp() throws Exception {
    165         super.setUp();
    166         final WebViewCtsActivity activity = getActivity();
    167         mWebView = activity.getWebView();
    168         if (mWebView != null) {
    169             new PollingCheck() {
    170                 @Override
    171                     protected boolean check() {
    172                         return activity.hasWindowFocus();
    173                 }
    174             }.run();
    175             File f = activity.getFileStreamPath("snapshot");
    176             if (f.exists()) {
    177                 f.delete();
    178             }
    179 
    180             mOnUiThread = new WebViewOnUiThread(this, mWebView);
    181         }
    182     }
    183 
    184     @Override
    185     protected void tearDown() throws Exception {
    186         if (mOnUiThread != null) {
    187             mOnUiThread.cleanUp();
    188         }
    189         if (mWebServer != null) {
    190             stopWebServer();
    191         }
    192         if (mIconDb != null) {
    193             mIconDb.removeAllIcons();
    194             mIconDb.close();
    195             mIconDb = null;
    196         }
    197         super.tearDown();
    198     }
    199 
    200     private void startWebServer(boolean secure) throws Exception {
    201         assertNull(mWebServer);
    202         mWebServer = new CtsTestServer(getActivity(), secure);
    203     }
    204 
    205     private void stopWebServer() throws Exception {
    206         assertNotNull(mWebServer);
    207         ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
    208         ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy)
    209                 .permitNetwork()
    210                 .build();
    211         StrictMode.setThreadPolicy(tmpPolicy);
    212         mWebServer.shutdown();
    213         mWebServer = null;
    214         StrictMode.setThreadPolicy(oldPolicy);
    215     }
    216 
    217     @UiThreadTest
    218     public void testConstructor() {
    219         if (!NullWebViewUtils.isWebViewAvailable()) {
    220             return;
    221         }
    222 
    223         new WebView(getActivity());
    224         new WebView(getActivity(), null);
    225         new WebView(getActivity(), null, 0);
    226     }
    227 
    228     @UiThreadTest
    229     public void testCreatingWebViewWithDeviceEncrpytionFails() {
    230         if (!NullWebViewUtils.isWebViewAvailable()) {
    231             return;
    232         }
    233 
    234         Context deviceEncryptedContext = getActivity().createDeviceProtectedStorageContext();
    235         try {
    236             new WebView(deviceEncryptedContext);
    237         } catch (IllegalArgumentException e) {
    238             return;
    239         }
    240 
    241         Assert.fail("WebView should have thrown exception when creating with a device " +
    242                 "protected storage context");
    243     }
    244 
    245     @UiThreadTest
    246     public void testCreatingWebViewWithMultipleEncryptionContext() {
    247         if (!NullWebViewUtils.isWebViewAvailable()) {
    248             return;
    249         }
    250 
    251         // Credential encrpytion is the default. Create one here for the sake of clarity.
    252         Context credentialEncryptedContext = getActivity().createCredentialProtectedStorageContext();
    253         Context deviceEncryptedContext = getActivity().createDeviceProtectedStorageContext();
    254 
    255         // No exception should be thrown with credential encryption context.
    256         new WebView(credentialEncryptedContext);
    257 
    258         try {
    259             new WebView(deviceEncryptedContext);
    260         } catch (IllegalArgumentException e) {
    261             return;
    262         }
    263 
    264         Assert.fail("WebView should have thrown exception when creating with a device " +
    265                 "protected storage context");
    266     }
    267 
    268     @UiThreadTest
    269     public void testCreatingWebViewCreatesCookieSyncManager() throws Exception {
    270         if (!NullWebViewUtils.isWebViewAvailable()) {
    271             return;
    272         }
    273         new WebView(getActivity());
    274         assertNotNull(CookieSyncManager.getInstance());
    275     }
    276 
    277     // Static methods should be safe to call on non-UI threads
    278     public void testFindAddress() {
    279         if (!NullWebViewUtils.isWebViewAvailable()) {
    280             return;
    281         }
    282 
    283         /*
    284          * Info about USPS
    285          * http://en.wikipedia.org/wiki/Postal_address#United_States
    286          * http://www.usps.com/
    287          */
    288         // full address
    289         assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826",
    290                 WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826"));
    291         // Zipcode is optional.
    292         assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA",
    293                 WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA"));
    294         // not an address
    295         assertNull(WebView.findAddress("This is not an address: no town, no state, no zip."));
    296     }
    297 
    298     @SuppressWarnings("deprecation")
    299     @UiThreadTest
    300     public void testGetZoomControls() {
    301         if (!NullWebViewUtils.isWebViewAvailable()) {
    302             return;
    303         }
    304         WebSettings settings = mWebView.getSettings();
    305         assertTrue(settings.supportZoom());
    306         View zoomControls = mWebView.getZoomControls();
    307         assertNotNull(zoomControls);
    308 
    309         // disable zoom support
    310         settings.setSupportZoom(false);
    311         assertFalse(settings.supportZoom());
    312         assertNull(mWebView.getZoomControls());
    313     }
    314 
    315     @UiThreadTest
    316     public void testInvokeZoomPicker() throws Exception {
    317         if (!NullWebViewUtils.isWebViewAvailable()) {
    318             return;
    319         }
    320         WebSettings settings = mWebView.getSettings();
    321         assertTrue(settings.supportZoom());
    322         startWebServer(false);
    323         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    324         mOnUiThread.loadUrlAndWaitForCompletion(url);
    325         mWebView.invokeZoomPicker();
    326     }
    327 
    328     public void testZoom() throws Throwable {
    329         if (!NullWebViewUtils.isWebViewAvailable()) {
    330             return;
    331         }
    332 
    333         // Pinch zoom is not supported in wrap_content layouts.
    334         mOnUiThread.setLayoutHeightToMatchParent();
    335 
    336         final ScaleChangedWebViewClient webViewClient = new ScaleChangedWebViewClient();
    337         mOnUiThread.setWebViewClient(webViewClient);
    338 
    339         mWebServer = new CtsTestServer(getActivity());
    340         mOnUiThread.loadUrlAndWaitForCompletion(
    341                 mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
    342         pollingCheckForCanZoomIn();
    343 
    344         WebSettings settings = mOnUiThread.getSettings();
    345         settings.setSupportZoom(false);
    346         assertFalse(settings.supportZoom());
    347         float currScale = mOnUiThread.getScale();
    348         float previousScale = currScale;
    349 
    350         // can zoom in or out although zoom support is disabled in web settings
    351         assertTrue(mOnUiThread.zoomIn());
    352         webViewClient.waitForScaleChanged();
    353 
    354         currScale = mOnUiThread.getScale();
    355         assertTrue(currScale > previousScale);
    356 
    357         assertTrue(mOnUiThread.zoomOut());
    358         previousScale = currScale;
    359         webViewClient.waitForScaleChanged();
    360 
    361         currScale = mOnUiThread.getScale();
    362         assertTrue(currScale < previousScale);
    363 
    364         mOnUiThread.zoomBy(1.25f); // zoom in
    365         previousScale = currScale;
    366         webViewClient.waitForScaleChanged();
    367 
    368         currScale = mOnUiThread.getScale();
    369         assertTrue(currScale > previousScale);
    370 
    371         mOnUiThread.zoomBy(0.8f); // zoom out
    372         previousScale = currScale;
    373         webViewClient.waitForScaleChanged();
    374 
    375         currScale = mOnUiThread.getScale();
    376         assertTrue(currScale < previousScale);
    377 
    378         // enable zoom support
    379         settings.setSupportZoom(true);
    380         assertTrue(settings.supportZoom());
    381         previousScale = mOnUiThread.getScale();
    382 
    383         assertTrue(mOnUiThread.zoomIn());
    384         webViewClient.waitForScaleChanged();
    385 
    386         currScale = mOnUiThread.getScale();
    387         assertTrue(currScale > previousScale);
    388 
    389         // zoom in until it reaches maximum scale
    390         while (mOnUiThread.zoomIn()) {
    391             previousScale = currScale;
    392             webViewClient.waitForScaleChanged();
    393             currScale = mOnUiThread.getScale();
    394             assertTrue(currScale > previousScale);
    395         }
    396 
    397         previousScale = currScale;
    398         // can not zoom in further
    399         assertFalse(mOnUiThread.zoomIn());
    400 
    401         // We sleep to assert to the best of our ability
    402         // that a scale change does *not* happen.
    403         Thread.sleep(500);
    404         currScale = mOnUiThread.getScale();
    405         assertEquals(currScale, previousScale, PAGE_SCALE_EPSILON);
    406 
    407         assertTrue(mOnUiThread.zoomOut());
    408         previousScale = currScale;
    409         webViewClient.waitForScaleChanged();
    410 
    411         currScale = mOnUiThread.getScale();
    412         assertTrue(currScale < previousScale);
    413 
    414         // zoom out until it reaches minimum scale
    415         while (mOnUiThread.zoomOut()) {
    416             previousScale = currScale;
    417             webViewClient.waitForScaleChanged();
    418             currScale = mOnUiThread.getScale();
    419             assertTrue(currScale < previousScale);
    420         }
    421 
    422         previousScale = currScale;
    423         assertFalse(mOnUiThread.zoomOut());
    424 
    425         // We sleep to assert to the best of our ability
    426         // that a scale change does *not* happen.
    427         Thread.sleep(500);
    428         currScale = mOnUiThread.getScale();
    429         assertEquals(currScale, previousScale, PAGE_SCALE_EPSILON);
    430 
    431         mOnUiThread.zoomBy(1.25f);
    432         previousScale = currScale;
    433         webViewClient.waitForScaleChanged();
    434 
    435         currScale = mOnUiThread.getScale();
    436         assertTrue(currScale > previousScale);
    437 
    438         // zoom in until it reaches maximum scale
    439         while (mOnUiThread.canZoomIn()) {
    440             previousScale = currScale;
    441             mOnUiThread.zoomBy(1.25f);
    442             webViewClient.waitForScaleChanged();
    443             currScale = mOnUiThread.getScale();
    444             assertTrue(currScale > previousScale);
    445         }
    446 
    447         previousScale = currScale;
    448 
    449         // We sleep to assert to the best of our ability
    450         // that a scale change does *not* happen.
    451         Thread.sleep(500);
    452         currScale = mOnUiThread.getScale();
    453         assertEquals(currScale, previousScale, PAGE_SCALE_EPSILON);
    454 
    455         mOnUiThread.zoomBy(0.8f);
    456         previousScale = currScale;
    457         webViewClient.waitForScaleChanged();
    458 
    459         currScale = mOnUiThread.getScale();
    460         assertTrue(currScale < previousScale);
    461 
    462         // zoom out until it reaches minimum scale
    463         while (mOnUiThread.canZoomOut()) {
    464             previousScale = currScale;
    465             mOnUiThread.zoomBy(0.8f);
    466             webViewClient.waitForScaleChanged();
    467             currScale = mOnUiThread.getScale();
    468             assertTrue(currScale < previousScale);
    469         }
    470 
    471         previousScale = currScale;
    472 
    473         // We sleep to assert to the best of our ability
    474         // that a scale change does *not* happen.
    475         Thread.sleep(500);
    476         currScale = mOnUiThread.getScale();
    477         assertEquals(currScale, previousScale, PAGE_SCALE_EPSILON);
    478     }
    479 
    480     @UiThreadTest
    481     public void testScrollBarOverlay() throws Throwable {
    482         if (!NullWebViewUtils.isWebViewAvailable()) {
    483             return;
    484         }
    485 
    486         // These functions have no effect; just verify they don't crash
    487         mWebView.setHorizontalScrollbarOverlay(true);
    488         mWebView.setVerticalScrollbarOverlay(false);
    489 
    490         assertTrue(mWebView.overlayHorizontalScrollbar());
    491         assertFalse(mWebView.overlayVerticalScrollbar());
    492     }
    493 
    494     @Presubmit
    495     @UiThreadTest
    496     public void testLoadUrl() throws Exception {
    497         if (!NullWebViewUtils.isWebViewAvailable()) {
    498             return;
    499         }
    500 
    501         assertNull(mWebView.getUrl());
    502         assertNull(mWebView.getOriginalUrl());
    503         assertEquals(INITIAL_PROGRESS, mWebView.getProgress());
    504 
    505         startWebServer(false);
    506         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    507         mOnUiThread.loadUrlAndWaitForCompletion(url);
    508         assertEquals(100, mWebView.getProgress());
    509         assertEquals(url, mWebView.getUrl());
    510         assertEquals(url, mWebView.getOriginalUrl());
    511         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle());
    512 
    513         // verify that the request also includes X-Requested-With header
    514         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
    515         Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
    516         assertEquals(1, matchingHeaders.length);
    517 
    518         Header header = matchingHeaders[0];
    519         assertEquals(mWebView.getContext().getApplicationInfo().packageName, header.getValue());
    520     }
    521 
    522     @UiThreadTest
    523     public void testPostUrlWithNonNetworkUrl() throws Exception {
    524         if (!NullWebViewUtils.isWebViewAvailable()) {
    525             return;
    526         }
    527         final String nonNetworkUrl = "file:///android_asset/" + TestHtmlConstants.HELLO_WORLD_URL;
    528 
    529         mOnUiThread.postUrlAndWaitForCompletion(nonNetworkUrl, new byte[1]);
    530 
    531         // Test if the nonNetworkUrl is loaded
    532         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle());
    533     }
    534 
    535     @UiThreadTest
    536     public void testPostUrlWithNetworkUrl() throws Exception {
    537         if (!NullWebViewUtils.isWebViewAvailable()) {
    538             return;
    539         }
    540         startWebServer(false);
    541         final String networkUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    542         final String postDataString = "username=my_username&password=my_password";
    543         final byte[] postData = EncodingUtils.getBytes(postDataString, "BASE64");
    544 
    545         mOnUiThread.postUrlAndWaitForCompletion(networkUrl, postData);
    546 
    547         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
    548         // The last request should be POST
    549         assertEquals(request.getRequestLine().getMethod(), "POST");
    550 
    551         // The last request should have a request body
    552         assertTrue(request instanceof HttpEntityEnclosingRequest);
    553         HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
    554         String entityString = EntityUtils.toString(entity);
    555         assertEquals(entityString, postDataString);
    556     }
    557 
    558     @UiThreadTest
    559     public void testLoadUrlDoesNotStripParamsWhenLoadingContentUrls() throws Exception {
    560         if (!NullWebViewUtils.isWebViewAvailable()) {
    561             return;
    562         }
    563 
    564         Uri.Builder uriBuilder = new Uri.Builder().scheme(
    565                 ContentResolver.SCHEME_CONTENT).authority(MockContentProvider.AUTHORITY);
    566         uriBuilder.appendPath("foo.html").appendQueryParameter("param","bar");
    567         String url = uriBuilder.build().toString();
    568         mOnUiThread.loadUrlAndWaitForCompletion(url);
    569         // verify the parameter is not stripped.
    570         Uri uri = Uri.parse(mWebView.getTitle());
    571         assertEquals("bar", uri.getQueryParameter("param"));
    572     }
    573 
    574     @UiThreadTest
    575     public void testAppInjectedXRequestedWithHeaderIsNotOverwritten() throws Exception {
    576         if (!NullWebViewUtils.isWebViewAvailable()) {
    577             return;
    578         }
    579 
    580         startWebServer(false);
    581         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    582         HashMap<String, String> map = new HashMap<String, String>();
    583         final String requester = "foo";
    584         map.put(X_REQUESTED_WITH, requester);
    585         mOnUiThread.loadUrlAndWaitForCompletion(url, map);
    586 
    587         // verify that the request also includes X-Requested-With header
    588         // but is not overwritten by the webview
    589         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
    590         Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
    591         assertEquals(1, matchingHeaders.length);
    592 
    593         Header header = matchingHeaders[0];
    594         assertEquals(requester, header.getValue());
    595     }
    596 
    597     @UiThreadTest
    598     public void testAppCanInjectHeadersViaImmutableMap() throws Exception {
    599         if (!NullWebViewUtils.isWebViewAvailable()) {
    600             return;
    601         }
    602 
    603         startWebServer(false);
    604         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    605         HashMap<String, String> map = new HashMap<String, String>();
    606         final String requester = "foo";
    607         map.put(X_REQUESTED_WITH, requester);
    608         mOnUiThread.loadUrlAndWaitForCompletion(url, Collections.unmodifiableMap(map));
    609 
    610         // verify that the request also includes X-Requested-With header
    611         // but is not overwritten by the webview
    612         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
    613         Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
    614         assertEquals(1, matchingHeaders.length);
    615 
    616         Header header = matchingHeaders[0];
    617         assertEquals(requester, header.getValue());
    618     }
    619 
    620     public void testCanInjectHeaders() throws Exception {
    621         if (!NullWebViewUtils.isWebViewAvailable()) {
    622             return;
    623         }
    624 
    625         final String X_FOO = "X-foo";
    626         final String X_FOO_VALUE = "test";
    627 
    628         final String X_REFERER = "Referer";
    629         final String X_REFERER_VALUE = "http://www.example.com/";
    630         startWebServer(false);
    631         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    632         HashMap<String, String> map = new HashMap<String, String>();
    633         map.put(X_FOO, X_FOO_VALUE);
    634         map.put(X_REFERER, X_REFERER_VALUE);
    635         mOnUiThread.loadUrlAndWaitForCompletion(url, map);
    636 
    637         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
    638         for (Map.Entry<String,String> value : map.entrySet()) {
    639             String header = value.getKey();
    640             Header[] matchingHeaders = request.getHeaders(header);
    641             assertEquals("header " + header + " not found", 1, matchingHeaders.length);
    642             assertEquals(value.getValue(), matchingHeaders[0].getValue());
    643         }
    644     }
    645 
    646     @SuppressWarnings("deprecation")
    647     @UiThreadTest
    648     public void testGetVisibleTitleHeight() throws Exception {
    649         if (!NullWebViewUtils.isWebViewAvailable()) {
    650             return;
    651         }
    652 
    653         startWebServer(false);
    654         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    655         mOnUiThread.loadUrlAndWaitForCompletion(url);
    656         assertEquals(0, mWebView.getVisibleTitleHeight());
    657     }
    658 
    659     @UiThreadTest
    660     public void testGetOriginalUrl() throws Throwable {
    661         if (!NullWebViewUtils.isWebViewAvailable()) {
    662             return;
    663         }
    664 
    665         startWebServer(false);
    666         final String finalUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    667         final String redirectUrl =
    668                 mWebServer.getRedirectingAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    669 
    670         assertNull(mWebView.getUrl());
    671         assertNull(mWebView.getOriginalUrl());
    672 
    673         // By default, WebView sends an intent to ask the system to
    674         // handle loading a new URL. We set a WebViewClient as
    675         // WebViewClient.shouldOverrideUrlLoading() returns false, so
    676         // the WebView will load the new URL.
    677         mOnUiThread.setWebViewClient(new WaitForLoadedClient(mOnUiThread));
    678         mOnUiThread.loadUrlAndWaitForCompletion(redirectUrl);
    679 
    680         assertEquals(finalUrl, mWebView.getUrl());
    681         assertEquals(redirectUrl, mWebView.getOriginalUrl());
    682     }
    683 
    684     public void testStopLoading() throws Exception {
    685         if (!NullWebViewUtils.isWebViewAvailable()) {
    686             return;
    687         }
    688 
    689         assertEquals(INITIAL_PROGRESS, mOnUiThread.getProgress());
    690 
    691         startWebServer(false);
    692         String url = mWebServer.getDelayedAssetUrl(TestHtmlConstants.STOP_LOADING_URL);
    693 
    694         class JsInterface {
    695             private boolean mPageLoaded;
    696 
    697             @JavascriptInterface
    698             public synchronized void pageLoaded() {
    699                 mPageLoaded = true;
    700                 notify();
    701             }
    702             public synchronized boolean getPageLoaded() {
    703                 return mPageLoaded;
    704             }
    705         }
    706 
    707         JsInterface jsInterface = new JsInterface();
    708 
    709         mOnUiThread.getSettings().setJavaScriptEnabled(true);
    710         mOnUiThread.addJavascriptInterface(jsInterface, "javabridge");
    711         mOnUiThread.loadUrl(url);
    712         mOnUiThread.stopLoading();
    713 
    714         // We wait to see that the onload callback in the HTML is not fired.
    715         synchronized (jsInterface) {
    716             jsInterface.wait(3000);
    717         }
    718 
    719         assertFalse(jsInterface.getPageLoaded());
    720     }
    721 
    722     @UiThreadTest
    723     public void testGoBackAndForward() throws Exception {
    724         if (!NullWebViewUtils.isWebViewAvailable()) {
    725             return;
    726         }
    727 
    728         assertGoBackOrForwardBySteps(false, -1);
    729         assertGoBackOrForwardBySteps(false, 1);
    730 
    731         startWebServer(false);
    732         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
    733         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
    734         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
    735 
    736         mOnUiThread.loadUrlAndWaitForCompletion(url1);
    737         pollingCheckWebBackForwardList(url1, 0, 1);
    738         assertGoBackOrForwardBySteps(false, -1);
    739         assertGoBackOrForwardBySteps(false, 1);
    740 
    741         mOnUiThread.loadUrlAndWaitForCompletion(url2);
    742         pollingCheckWebBackForwardList(url2, 1, 2);
    743         assertGoBackOrForwardBySteps(true, -1);
    744         assertGoBackOrForwardBySteps(false, 1);
    745 
    746         mOnUiThread.loadUrlAndWaitForCompletion(url3);
    747         pollingCheckWebBackForwardList(url3, 2, 3);
    748         assertGoBackOrForwardBySteps(true, -2);
    749         assertGoBackOrForwardBySteps(false, 1);
    750 
    751         mWebView.goBack();
    752         pollingCheckWebBackForwardList(url2, 1, 3);
    753         assertGoBackOrForwardBySteps(true, -1);
    754         assertGoBackOrForwardBySteps(true, 1);
    755 
    756         mWebView.goForward();
    757         pollingCheckWebBackForwardList(url3, 2, 3);
    758         assertGoBackOrForwardBySteps(true, -2);
    759         assertGoBackOrForwardBySteps(false, 1);
    760 
    761         mWebView.goBackOrForward(-2);
    762         pollingCheckWebBackForwardList(url1, 0, 3);
    763         assertGoBackOrForwardBySteps(false, -1);
    764         assertGoBackOrForwardBySteps(true, 2);
    765 
    766         mWebView.goBackOrForward(2);
    767         pollingCheckWebBackForwardList(url3, 2, 3);
    768         assertGoBackOrForwardBySteps(true, -2);
    769         assertGoBackOrForwardBySteps(false, 1);
    770     }
    771 
    772     @UiThreadTest
    773     public void testAddJavascriptInterface() throws Exception {
    774         if (!NullWebViewUtils.isWebViewAvailable()) {
    775             return;
    776         }
    777 
    778         WebSettings settings = mWebView.getSettings();
    779         settings.setJavaScriptEnabled(true);
    780         settings.setJavaScriptCanOpenWindowsAutomatically(true);
    781 
    782         final class DummyJavaScriptInterface {
    783             private boolean mWasProvideResultCalled;
    784             private String mResult;
    785 
    786             private synchronized String waitForResult() {
    787                 while (!mWasProvideResultCalled) {
    788                     try {
    789                         wait(TEST_TIMEOUT);
    790                     } catch (InterruptedException e) {
    791                         continue;
    792                     }
    793                     if (!mWasProvideResultCalled) {
    794                         Assert.fail("Unexpected timeout");
    795                     }
    796                 }
    797                 return mResult;
    798             }
    799 
    800             public synchronized boolean wasProvideResultCalled() {
    801                 return mWasProvideResultCalled;
    802             }
    803 
    804             @JavascriptInterface
    805             public synchronized void provideResult(String result) {
    806                 mWasProvideResultCalled = true;
    807                 mResult = result;
    808                 notify();
    809             }
    810         }
    811 
    812         final DummyJavaScriptInterface obj = new DummyJavaScriptInterface();
    813         mWebView.addJavascriptInterface(obj, "dummy");
    814         assertFalse(obj.wasProvideResultCalled());
    815 
    816         startWebServer(false);
    817         String url = mWebServer.getAssetUrl(TestHtmlConstants.ADD_JAVA_SCRIPT_INTERFACE_URL);
    818         mOnUiThread.loadUrlAndWaitForCompletion(url);
    819         assertEquals("Original title", obj.waitForResult());
    820 
    821         // Verify that only methods annotated with @JavascriptInterface are exposed
    822         // on the JavaScript interface object.
    823         mOnUiThread.evaluateJavascript("typeof dummy.provideResult",
    824                 new ValueCallback<String>() {
    825                     @Override
    826                     public void onReceiveValue(String result) {
    827                         assertEquals("\"function\"", result);
    828                     }
    829                 });
    830         mOnUiThread.evaluateJavascript("typeof dummy.wasProvideResultCalled",
    831                 new ValueCallback<String>() {
    832                     @Override
    833                     public void onReceiveValue(String result) {
    834                         assertEquals("\"undefined\"", result);
    835                     }
    836                 });
    837         mOnUiThread.evaluateJavascript("typeof dummy.getClass",
    838                 new ValueCallback<String>() {
    839                     @Override
    840                     public void onReceiveValue(String result) {
    841                         assertEquals("\"undefined\"", result);
    842                     }
    843                 });
    844     }
    845 
    846     @UiThreadTest
    847     public void testAddJavascriptInterfaceNullObject() throws Exception {
    848         if (!NullWebViewUtils.isWebViewAvailable()) {
    849             return;
    850         }
    851 
    852         WebSettings settings = mWebView.getSettings();
    853         settings.setJavaScriptEnabled(true);
    854         String setTitleToPropertyTypeHtml = "<html><head></head>" +
    855                 "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>";
    856 
    857         // Test that the property is initially undefined.
    858         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    859                 "text/html", null);
    860         assertEquals("undefined", mWebView.getTitle());
    861 
    862         // Test that adding a null object has no effect.
    863         mWebView.addJavascriptInterface(null, "injectedObject");
    864         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    865                 "text/html", null);
    866         assertEquals("undefined", mWebView.getTitle());
    867 
    868         // Test that adding an object gives an object type.
    869         final Object obj = new Object();
    870         mWebView.addJavascriptInterface(obj, "injectedObject");
    871         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    872                 "text/html", null);
    873         assertEquals("object", mWebView.getTitle());
    874 
    875         // Test that trying to replace with a null object has no effect.
    876         mWebView.addJavascriptInterface(null, "injectedObject");
    877         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    878                 "text/html", null);
    879         assertEquals("object", mWebView.getTitle());
    880     }
    881 
    882     @UiThreadTest
    883     public void testRemoveJavascriptInterface() throws Exception {
    884         if (!NullWebViewUtils.isWebViewAvailable()) {
    885             return;
    886         }
    887 
    888         WebSettings settings = mWebView.getSettings();
    889         settings.setJavaScriptEnabled(true);
    890         String setTitleToPropertyTypeHtml = "<html><head></head>" +
    891                 "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>";
    892 
    893         // Test that adding an object gives an object type.
    894         mWebView.addJavascriptInterface(new Object(), "injectedObject");
    895         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    896                 "text/html", null);
    897         assertEquals("object", mWebView.getTitle());
    898 
    899         // Test that reloading the page after removing the object leaves the property undefined.
    900         mWebView.removeJavascriptInterface("injectedObject");
    901         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    902                 "text/html", null);
    903         assertEquals("undefined", mWebView.getTitle());
    904     }
    905 
    906     public void testUseRemovedJavascriptInterface() throws Throwable {
    907         if (!NullWebViewUtils.isWebViewAvailable()) {
    908             return;
    909         }
    910 
    911         class RemovedObject {
    912             @Override
    913             @JavascriptInterface
    914             public String toString() {
    915                 return "removedObject";
    916             }
    917 
    918             @JavascriptInterface
    919             public void remove() throws Throwable {
    920                 mOnUiThread.removeJavascriptInterface("removedObject");
    921                 System.gc();
    922             }
    923         }
    924         class ResultObject {
    925             private String mResult;
    926             private boolean mIsResultAvailable;
    927 
    928             @JavascriptInterface
    929             public synchronized void setResult(String result) {
    930                 mResult = result;
    931                 mIsResultAvailable = true;
    932                 notify();
    933             }
    934             public synchronized String getResult() {
    935                 while (!mIsResultAvailable) {
    936                     try {
    937                         wait();
    938                     } catch (InterruptedException e) {
    939                     }
    940                 }
    941                 return mResult;
    942             }
    943         }
    944         final ResultObject resultObject = new ResultObject();
    945 
    946         // Test that an object is still usable if removed while the page is in use, even if we have
    947         // no external references to it.
    948         mOnUiThread.getSettings().setJavaScriptEnabled(true);
    949         mOnUiThread.addJavascriptInterface(new RemovedObject(), "removedObject");
    950         mOnUiThread.addJavascriptInterface(resultObject, "resultObject");
    951         mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" +
    952                 "<body onload=\"window.removedObject.remove();" +
    953                 "resultObject.setResult(removedObject.toString());\"></body></html>",
    954                 "text/html", null);
    955         assertEquals("removedObject", resultObject.getResult());
    956     }
    957 
    958     public void testAddJavascriptInterfaceExceptions() throws Exception {
    959         if (!NullWebViewUtils.isWebViewAvailable()) {
    960             return;
    961         }
    962         WebSettings settings = mOnUiThread.getSettings();
    963         settings.setJavaScriptEnabled(true);
    964         settings.setJavaScriptCanOpenWindowsAutomatically(true);
    965 
    966         final AtomicBoolean mJsInterfaceWasCalled = new AtomicBoolean(false) {
    967             @JavascriptInterface
    968             public synchronized void call() {
    969                 set(true);
    970                 // The main purpose of this test is to ensure an exception here does not
    971                 // crash the implementation.
    972                 throw new RuntimeException("Javascript Interface exception");
    973             }
    974         };
    975 
    976         mOnUiThread.addJavascriptInterface(mJsInterfaceWasCalled, "dummy");
    977 
    978         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
    979 
    980         assertFalse(mJsInterfaceWasCalled.get());
    981 
    982         final CountDownLatch resultLatch = new CountDownLatch(1);
    983         mOnUiThread.evaluateJavascript(
    984                 "try {dummy.call(); 'fail'; } catch (exception) { 'pass'; } ",
    985                 new ValueCallback<String>() {
    986                         @Override
    987                         public void onReceiveValue(String result) {
    988                             assertEquals("\"pass\"", result);
    989                             resultLatch.countDown();
    990                         }
    991                     });
    992 
    993         assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
    994         assertTrue(mJsInterfaceWasCalled.get());
    995     }
    996 
    997     public void testJavascriptInterfaceCustomPropertiesClearedOnReload() throws Exception {
    998         if (!NullWebViewUtils.isWebViewAvailable()) {
    999             return;
   1000         }
   1001 
   1002         mOnUiThread.getSettings().setJavaScriptEnabled(true);
   1003 
   1004         class DummyJavaScriptInterface {
   1005         }
   1006         final DummyJavaScriptInterface obj = new DummyJavaScriptInterface();
   1007         mOnUiThread.addJavascriptInterface(obj, "dummy");
   1008         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
   1009 
   1010         EvaluateJsResultPollingCheck jsResult;
   1011         jsResult = new EvaluateJsResultPollingCheck("42");
   1012         mOnUiThread.evaluateJavascript("dummy.custom_property = 42", jsResult);
   1013         jsResult.run();
   1014         jsResult = new EvaluateJsResultPollingCheck("true");
   1015         mOnUiThread.evaluateJavascript("'custom_property' in dummy", jsResult);
   1016         jsResult.run();
   1017 
   1018         mOnUiThread.reloadAndWaitForCompletion();
   1019 
   1020         jsResult = new EvaluateJsResultPollingCheck("false");
   1021         mOnUiThread.evaluateJavascript("'custom_property' in dummy", jsResult);
   1022         jsResult.run();
   1023     }
   1024 
   1025     public void testJavascriptInterfaceForClientPopup() throws Exception {
   1026         if (!NullWebViewUtils.isWebViewAvailable()) {
   1027             return;
   1028         }
   1029 
   1030         mOnUiThread.getSettings().setJavaScriptEnabled(true);
   1031         mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
   1032         mOnUiThread.getSettings().setSupportMultipleWindows(true);
   1033 
   1034         class DummyJavaScriptInterface {
   1035             @JavascriptInterface
   1036             public int test() {
   1037                 return 42;
   1038             }
   1039         }
   1040         final DummyJavaScriptInterface obj = new DummyJavaScriptInterface();
   1041 
   1042         final WebView childWebView = mOnUiThread.createWebView();
   1043         WebViewOnUiThread childOnUiThread = new WebViewOnUiThread(this, childWebView);
   1044         childOnUiThread.getSettings().setJavaScriptEnabled(true);
   1045         childOnUiThread.addJavascriptInterface(obj, "dummy");
   1046 
   1047         final boolean[] hadOnCreateWindow = new boolean[1];
   1048         hadOnCreateWindow[0] = false;
   1049         mOnUiThread.setWebChromeClient(new WebViewOnUiThread.WaitForProgressClient(mOnUiThread) {
   1050             @Override
   1051             public boolean onCreateWindow(
   1052                 WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
   1053                 getActivity().addContentView(childWebView, new ViewGroup.LayoutParams(
   1054                             ViewGroup.LayoutParams.FILL_PARENT,
   1055                             ViewGroup.LayoutParams.WRAP_CONTENT));
   1056                 WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
   1057                 transport.setWebView(childWebView);
   1058                 resultMsg.sendToTarget();
   1059                 hadOnCreateWindow[0] = true;
   1060                 return true;
   1061             }
   1062         });
   1063 
   1064         startWebServer(false);
   1065         mOnUiThread.loadUrlAndWaitForCompletion(mWebServer.
   1066                 getAssetUrl(TestHtmlConstants.POPUP_URL));
   1067         new PollingCheck(TEST_TIMEOUT) {
   1068             @Override
   1069             protected boolean check() {
   1070                 return hadOnCreateWindow[0];
   1071             }
   1072         }.run();
   1073 
   1074         childOnUiThread.loadUrlAndWaitForCompletion("about:blank");
   1075         EvaluateJsResultPollingCheck jsResult;
   1076         jsResult = new EvaluateJsResultPollingCheck("true");
   1077         childOnUiThread.evaluateJavascript("'dummy' in window", jsResult);
   1078         jsResult.run();
   1079         // Verify that the injected object is functional.
   1080         jsResult = new EvaluateJsResultPollingCheck("42");
   1081         childOnUiThread.evaluateJavascript("dummy.test()", jsResult);
   1082         jsResult.run();
   1083     }
   1084 
   1085     private final class TestPictureListener implements PictureListener {
   1086         public int callCount;
   1087 
   1088         @Override
   1089         public void onNewPicture(WebView view, Picture picture) {
   1090             // Need to inform the listener tracking new picture
   1091             // for the "page loaded" knowledge since it has been replaced.
   1092             mOnUiThread.onNewPicture();
   1093             this.callCount += 1;
   1094         }
   1095     }
   1096 
   1097     private Picture waitForPictureToHaveColor(int color,
   1098             final TestPictureListener listener) throws Throwable {
   1099         final int MAX_ON_NEW_PICTURE_ITERATIONS = 5;
   1100         final AtomicReference<Picture> pictureRef = new AtomicReference<Picture>();
   1101         for (int i = 0; i < MAX_ON_NEW_PICTURE_ITERATIONS; i++) {
   1102             final int oldCallCount = listener.callCount;
   1103             runTestOnUiThread(new Runnable() {
   1104                 @Override
   1105                 public void run() {
   1106                     pictureRef.set(mWebView.capturePicture());
   1107                 }
   1108             });
   1109             if (isPictureFilledWithColor(pictureRef.get(), color))
   1110                 break;
   1111             new PollingCheck(TEST_TIMEOUT) {
   1112                 @Override
   1113                 protected boolean check() {
   1114                     return listener.callCount > oldCallCount;
   1115                 }
   1116             }.run();
   1117         }
   1118         return pictureRef.get();
   1119     }
   1120 
   1121     public void testCapturePicture() throws Exception, Throwable {
   1122         if (!NullWebViewUtils.isWebViewAvailable()) {
   1123             return;
   1124         }
   1125         final TestPictureListener listener = new TestPictureListener();
   1126 
   1127         startWebServer(false);
   1128         final String url = mWebServer.getAssetUrl(TestHtmlConstants.BLANK_PAGE_URL);
   1129         mOnUiThread.setPictureListener(listener);
   1130         // Showing the blank page will fill the picture with the background color.
   1131         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1132         // The default background color is white.
   1133         Picture oldPicture = waitForPictureToHaveColor(Color.WHITE, listener);
   1134 
   1135         runTestOnUiThread(new Runnable() {
   1136             @Override
   1137             public void run() {
   1138                 mWebView.setBackgroundColor(Color.CYAN);
   1139             }
   1140         });
   1141         mOnUiThread.reloadAndWaitForCompletion();
   1142         waitForPictureToHaveColor(Color.CYAN, listener);
   1143 
   1144         // The content of the previously captured picture will not be updated automatically.
   1145         assertTrue(isPictureFilledWithColor(oldPicture, Color.WHITE));
   1146     }
   1147 
   1148     public void testSetPictureListener() throws Exception, Throwable {
   1149         if (!NullWebViewUtils.isWebViewAvailable()) {
   1150             return;
   1151         }
   1152         final class MyPictureListener implements PictureListener {
   1153             public int callCount;
   1154             public WebView webView;
   1155             public Picture picture;
   1156 
   1157             @Override
   1158             public void onNewPicture(WebView view, Picture picture) {
   1159                 // Need to inform the listener tracking new picture
   1160                 // for the "page loaded" knowledge since it has been replaced.
   1161                 mOnUiThread.onNewPicture();
   1162                 this.callCount += 1;
   1163                 this.webView = view;
   1164                 this.picture = picture;
   1165             }
   1166         }
   1167 
   1168         final MyPictureListener listener = new MyPictureListener();
   1169         startWebServer(false);
   1170         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1171         mOnUiThread.setPictureListener(listener);
   1172         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1173         new PollingCheck(TEST_TIMEOUT) {
   1174             @Override
   1175             protected boolean check() {
   1176                 return listener.callCount > 0;
   1177             }
   1178         }.run();
   1179         assertEquals(mWebView, listener.webView);
   1180         assertNull(listener.picture);
   1181 
   1182         final int oldCallCount = listener.callCount;
   1183         final String newUrl = mWebServer.getAssetUrl(TestHtmlConstants.SMALL_IMG_URL);
   1184         mOnUiThread.loadUrlAndWaitForCompletion(newUrl);
   1185         new PollingCheck(TEST_TIMEOUT) {
   1186             @Override
   1187             protected boolean check() {
   1188                 return listener.callCount > oldCallCount;
   1189             }
   1190         }.run();
   1191     }
   1192 
   1193     @UiThreadTest
   1194     public void testAccessHttpAuthUsernamePassword() {
   1195         if (!NullWebViewUtils.isWebViewAvailable()) {
   1196             return;
   1197         }
   1198         try {
   1199             WebViewDatabase.getInstance(getActivity()).clearHttpAuthUsernamePassword();
   1200 
   1201             String host = "http://localhost:8080";
   1202             String realm = "testrealm";
   1203             String userName = "user";
   1204             String password = "password";
   1205 
   1206             String[] result = mWebView.getHttpAuthUsernamePassword(host, realm);
   1207             assertNull(result);
   1208 
   1209             mWebView.setHttpAuthUsernamePassword(host, realm, userName, password);
   1210             result = mWebView.getHttpAuthUsernamePassword(host, realm);
   1211             assertNotNull(result);
   1212             assertEquals(userName, result[0]);
   1213             assertEquals(password, result[1]);
   1214 
   1215             String newPassword = "newpassword";
   1216             mWebView.setHttpAuthUsernamePassword(host, realm, userName, newPassword);
   1217             result = mWebView.getHttpAuthUsernamePassword(host, realm);
   1218             assertNotNull(result);
   1219             assertEquals(userName, result[0]);
   1220             assertEquals(newPassword, result[1]);
   1221 
   1222             String newUserName = "newuser";
   1223             mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
   1224             result = mWebView.getHttpAuthUsernamePassword(host, realm);
   1225             assertNotNull(result);
   1226             assertEquals(newUserName, result[0]);
   1227             assertEquals(newPassword, result[1]);
   1228 
   1229             // the user is set to null, can not change any thing in the future
   1230             mWebView.setHttpAuthUsernamePassword(host, realm, null, password);
   1231             result = mWebView.getHttpAuthUsernamePassword(host, realm);
   1232             assertNotNull(result);
   1233             assertNull(result[0]);
   1234             assertEquals(password, result[1]);
   1235 
   1236             mWebView.setHttpAuthUsernamePassword(host, realm, userName, null);
   1237             result = mWebView.getHttpAuthUsernamePassword(host, realm);
   1238             assertNotNull(result);
   1239             assertEquals(userName, result[0]);
   1240             assertEquals(null, result[1]);
   1241 
   1242             mWebView.setHttpAuthUsernamePassword(host, realm, null, null);
   1243             result = mWebView.getHttpAuthUsernamePassword(host, realm);
   1244             assertNotNull(result);
   1245             assertNull(result[0]);
   1246             assertNull(result[1]);
   1247 
   1248             mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
   1249             result = mWebView.getHttpAuthUsernamePassword(host, realm);
   1250             assertNotNull(result);
   1251             assertEquals(newUserName, result[0]);
   1252             assertEquals(newPassword, result[1]);
   1253         } finally {
   1254             WebViewDatabase.getInstance(getActivity()).clearHttpAuthUsernamePassword();
   1255         }
   1256     }
   1257 
   1258     @UiThreadTest
   1259     public void testWebViewDatabaseAccessHttpAuthUsernamePassword() {
   1260         if (!NullWebViewUtils.isWebViewAvailable()) {
   1261             return;
   1262         }
   1263         WebViewDatabase webViewDb = WebViewDatabase.getInstance(getActivity());
   1264         try {
   1265             webViewDb.clearHttpAuthUsernamePassword();
   1266 
   1267             String host = "http://localhost:8080";
   1268             String realm = "testrealm";
   1269             String userName = "user";
   1270             String password = "password";
   1271 
   1272             String[] result =
   1273                     mWebView.getHttpAuthUsernamePassword(host,
   1274                             realm);
   1275             assertNull(result);
   1276 
   1277             webViewDb.setHttpAuthUsernamePassword(host, realm, userName, password);
   1278             result = webViewDb.getHttpAuthUsernamePassword(host, realm);
   1279             assertNotNull(result);
   1280             assertEquals(userName, result[0]);
   1281             assertEquals(password, result[1]);
   1282 
   1283             String newPassword = "newpassword";
   1284             webViewDb.setHttpAuthUsernamePassword(host, realm, userName, newPassword);
   1285             result = webViewDb.getHttpAuthUsernamePassword(host, realm);
   1286             assertNotNull(result);
   1287             assertEquals(userName, result[0]);
   1288             assertEquals(newPassword, result[1]);
   1289 
   1290             String newUserName = "newuser";
   1291             webViewDb.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
   1292             result = webViewDb.getHttpAuthUsernamePassword(host, realm);
   1293             assertNotNull(result);
   1294             assertEquals(newUserName, result[0]);
   1295             assertEquals(newPassword, result[1]);
   1296 
   1297             // the user is set to null, can not change any thing in the future
   1298             webViewDb.setHttpAuthUsernamePassword(host, realm, null, password);
   1299             result = webViewDb.getHttpAuthUsernamePassword(host, realm);
   1300             assertNotNull(result);
   1301             assertNull(result[0]);
   1302             assertEquals(password, result[1]);
   1303 
   1304             webViewDb.setHttpAuthUsernamePassword(host, realm, userName, null);
   1305             result = webViewDb.getHttpAuthUsernamePassword(host, realm);
   1306             assertNotNull(result);
   1307             assertEquals(userName, result[0]);
   1308             assertEquals(null, result[1]);
   1309 
   1310             webViewDb.setHttpAuthUsernamePassword(host, realm, null, null);
   1311             result = webViewDb.getHttpAuthUsernamePassword(host, realm);
   1312             assertNotNull(result);
   1313             assertNull(result[0]);
   1314             assertNull(result[1]);
   1315 
   1316             webViewDb.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
   1317             result = webViewDb.getHttpAuthUsernamePassword(host, realm);
   1318             assertNotNull(result);
   1319             assertEquals(newUserName, result[0]);
   1320             assertEquals(newPassword, result[1]);
   1321         } finally {
   1322             webViewDb.clearHttpAuthUsernamePassword();
   1323         }
   1324     }
   1325 
   1326     public void testLoadData() throws Throwable {
   1327         if (!NullWebViewUtils.isWebViewAvailable()) {
   1328             return;
   1329         }
   1330         final String HTML_CONTENT =
   1331                 "<html><head><title>Hello,World!</title></head><body></body>" +
   1332                 "</html>";
   1333         mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT,
   1334                 "text/html", null);
   1335         assertEquals("Hello,World!", mOnUiThread.getTitle());
   1336 
   1337         startWebServer(false);
   1338         final ChromeClient webChromeClient = new ChromeClient(mOnUiThread);
   1339         final String crossOriginUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1340         runTestOnUiThread(new Runnable() {
   1341             @Override
   1342             public void run() {
   1343                 mWebView.getSettings().setJavaScriptEnabled(true);
   1344                 mOnUiThread.setWebChromeClient(webChromeClient);
   1345                 mOnUiThread.loadDataAndWaitForCompletion(
   1346                         "<html><head></head><body onload=\"" +
   1347                         "document.title = " +
   1348                         "document.getElementById('frame').contentWindow.location.href;" +
   1349                         "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
   1350                         "text/html", null);
   1351             }
   1352         });
   1353         assertEquals(ConsoleMessage.MessageLevel.ERROR, webChromeClient.getMessageLevel(10000));
   1354     }
   1355 
   1356     @UiThreadTest
   1357     public void testLoadDataWithBaseUrl() throws Throwable {
   1358         if (!NullWebViewUtils.isWebViewAvailable()) {
   1359             return;
   1360         }
   1361         assertNull(mWebView.getUrl());
   1362         String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
   1363         // Snippet of HTML that will prevent favicon requests to the test server.
   1364         final String HTML_HEADER = "<html><head><link rel=\"shortcut icon\" href=\"#\" /></head>";
   1365 
   1366         // Trying to resolve a relative URL against a data URL without a base URL
   1367         // will fail and we won't make a request to the test web server.
   1368         // By using the test web server as the base URL we expect to see a request
   1369         // for the relative URL in the test server.
   1370         startWebServer(false);
   1371         String baseUrl = mWebServer.getAssetUrl("foo.html");
   1372         String historyUrl = "http://www.example.com/";
   1373         mWebServer.resetRequestState();
   1374         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
   1375                 HTML_HEADER + "<body><img src=\"" + imgUrl + "\"/></body></html>",
   1376                 "text/html", "UTF-8", historyUrl);
   1377         // Verify that the resource request makes it to the server.
   1378         assertTrue(mWebServer.wasResourceRequested(imgUrl));
   1379         assertEquals(historyUrl, mWebView.getUrl());
   1380 
   1381         // Check that reported URL is "about:blank" when supplied history URL
   1382         // is null.
   1383         imgUrl = TestHtmlConstants.LARGE_IMG_URL;
   1384         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
   1385                 HTML_HEADER + "<body><img src=\"" + imgUrl + "\"/></body></html>",
   1386                 "text/html", "UTF-8", null);
   1387         assertTrue(mWebServer.wasResourceRequested(imgUrl));
   1388         assertEquals("about:blank", mWebView.getUrl());
   1389 
   1390         // Test that JavaScript can access content from the same origin as the base URL.
   1391         mWebView.getSettings().setJavaScriptEnabled(true);
   1392         final String crossOriginUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1393         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
   1394                 HTML_HEADER + "<body onload=\"" +
   1395                 "document.title = document.getElementById('frame').contentWindow.location.href;" +
   1396                 "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
   1397                 "text/html", "UTF-8", null);
   1398         assertEquals(crossOriginUrl, mWebView.getTitle());
   1399 
   1400         // Check that when the base URL uses the 'data' scheme, a 'data' scheme URL is used and the
   1401         // history URL is ignored.
   1402         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
   1403                 HTML_HEADER + "<body>bar</body></html>", "text/html", "UTF-8",
   1404                 historyUrl);
   1405         assertTrue("URL: " + mWebView.getUrl(), mWebView.getUrl().indexOf("data:text/html") == 0);
   1406         assertTrue("URL: " + mWebView.getUrl(), mWebView.getUrl().indexOf("bar") > 0);
   1407 
   1408         // Check that when a non-data: base URL is used, we treat the String to load as
   1409         // a raw string and just dump it into the WebView, i.e. not decoding any URL entities.
   1410         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("http://www.foo.com",
   1411                 HTML_HEADER + "<title>Hello World%21</title><body>bar</body></html>",
   1412                 "text/html", "UTF-8", null);
   1413         assertEquals("Hello World%21", mOnUiThread.getTitle());
   1414 
   1415         // Check that when a data: base URL is used, we treat the String to load as a data: URL
   1416         // and run load steps such as decoding URL entities (i.e., contrary to the test case
   1417         // above.)
   1418         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
   1419                 HTML_HEADER + "<title>Hello World%21</title></html>", "text/html", "UTF-8", null);
   1420         assertEquals("Hello World!", mOnUiThread.getTitle());
   1421 
   1422         // Check the method is null input safe.
   1423         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(null, null, null, null, null);
   1424         assertEquals("about:blank", mOnUiThread.getUrl());
   1425     }
   1426 
   1427     private void deleteIfExists(File file) throws IOException {
   1428         if (file.exists()) {
   1429             file.delete();
   1430         }
   1431     }
   1432 
   1433     private String readTextFile(File file, Charset encoding)
   1434             throws FileNotFoundException, IOException {
   1435         FileInputStream stream = new FileInputStream(file);
   1436         byte[] bytes = new byte[(int)file.length()];
   1437         stream.read(bytes);
   1438         stream.close();
   1439         return new String(bytes, encoding);
   1440     }
   1441 
   1442     private void doSaveWebArchive(String baseName, boolean autoName, final String expectName)
   1443             throws Throwable {
   1444         final Semaphore saving = new Semaphore(0);
   1445         ValueCallback<String> callback = new ValueCallback<String>() {
   1446             @Override
   1447             public void onReceiveValue(String savedName) {
   1448                 assertEquals(expectName, savedName);
   1449                 saving.release();
   1450             }
   1451         };
   1452 
   1453         mOnUiThread.saveWebArchive(baseName, autoName, callback);
   1454         assertTrue(saving.tryAcquire(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   1455     }
   1456 
   1457     public void testSaveWebArchive() throws Throwable {
   1458         if (!NullWebViewUtils.isWebViewAvailable()) {
   1459             return;
   1460         }
   1461 
   1462         final String testPage = "testSaveWebArchive test page";
   1463 
   1464         File dir = getActivity().getFilesDir();
   1465         String dirStr = dir.toString();
   1466 
   1467         File test = new File(dir, "test.mht");
   1468         deleteIfExists(test);
   1469         String testStr = test.getAbsolutePath();
   1470 
   1471         File index = new File(dir, "index.mht");
   1472         deleteIfExists(index);
   1473         String indexStr = index.getAbsolutePath();
   1474 
   1475         File index1 = new File(dir, "index-1.mht");
   1476         deleteIfExists(index1);
   1477         String index1Str = index1.getAbsolutePath();
   1478 
   1479         mOnUiThread.loadDataAndWaitForCompletion(testPage, "text/html", "UTF-8");
   1480 
   1481         try {
   1482             // Save test.mht
   1483             doSaveWebArchive(testStr, false, testStr);
   1484 
   1485             // Check the contents of test.mht
   1486             String testMhtml = readTextFile(test, StandardCharsets.UTF_8);
   1487             assertTrue(testMhtml.contains(testPage));
   1488 
   1489             // Save index.mht
   1490             doSaveWebArchive(dirStr + "/", true, indexStr);
   1491 
   1492             // Check the contents of index.mht
   1493             String indexMhtml = readTextFile(index, StandardCharsets.UTF_8);
   1494             assertTrue(indexMhtml.contains(testPage));
   1495 
   1496             // Save index-1.mht since index.mht already exists
   1497             doSaveWebArchive(dirStr + "/", true, index1Str);
   1498 
   1499             // Check the contents of index-1.mht
   1500             String index1Mhtml = readTextFile(index1, StandardCharsets.UTF_8);
   1501             assertTrue(index1Mhtml.contains(testPage));
   1502 
   1503             // Try a file in a bogus directory
   1504             doSaveWebArchive("/bogus/path/test.mht", false, null);
   1505 
   1506             // Try a bogus directory
   1507             doSaveWebArchive("/bogus/path/", true, null);
   1508         } finally {
   1509             deleteIfExists(test);
   1510             deleteIfExists(index);
   1511             deleteIfExists(index1);
   1512         }
   1513     }
   1514 
   1515     private static class WaitForFindResultsListener extends FutureTask<Integer>
   1516             implements WebView.FindListener {
   1517         private final WebViewOnUiThread mWebViewOnUiThread;
   1518         private final int mMatchesWanted;
   1519         private final String mStringWanted;
   1520         private final boolean mRetry;
   1521 
   1522         public WaitForFindResultsListener(
   1523                 WebViewOnUiThread wv, String wanted, int matches, boolean retry) {
   1524             super(new Runnable() {
   1525                 @Override
   1526                 public void run() { }
   1527             }, null);
   1528             mWebViewOnUiThread = wv;
   1529             mMatchesWanted = matches;
   1530             mStringWanted = wanted;
   1531             mRetry = retry;
   1532         }
   1533 
   1534         @Override
   1535         public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
   1536                 boolean isDoneCounting) {
   1537             if (isDoneCounting) {
   1538                 //If mRetry set to true and matches aren't equal, call findAll again
   1539                 if (mRetry && numberOfMatches != mMatchesWanted) {
   1540                     mWebViewOnUiThread.findAll(mStringWanted);
   1541                 }
   1542                 else {
   1543                     set(numberOfMatches);
   1544                 }
   1545             }
   1546         }
   1547     }
   1548 
   1549     public void testFindAll()  throws Throwable {
   1550         if (!NullWebViewUtils.isWebViewAvailable()) {
   1551             return;
   1552         }
   1553         // Make the page scrollable, so we can detect the scrolling to make sure the
   1554         // content fully loaded.
   1555         mOnUiThread.setInitialScale(100);
   1556         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
   1557         int dimension = Math.max(metrics.widthPixels, metrics.heightPixels);
   1558         // create a paragraph high enough to take up the entire screen
   1559         String p = "<p style=\"height:" + dimension + "px;\">" +
   1560                 "Find all instances of find on the page and highlight them.</p>";
   1561 
   1562         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1563                 + "</body></html>", "text/html", null);
   1564 
   1565         WaitForFindResultsListener l = new WaitForFindResultsListener(mOnUiThread, "find", 2, true);
   1566         mOnUiThread.setFindListener(l);
   1567         mOnUiThread.findAll("find");
   1568         assertEquals(2, l.get(MIN_FIND_WAIT_MS, TimeUnit.MILLISECONDS).intValue());
   1569     }
   1570 
   1571     public void testFindNext() throws Throwable {
   1572         if (!NullWebViewUtils.isWebViewAvailable()) {
   1573             return;
   1574         }
   1575         // Reset the scaling so that finding the next "all" text will require scrolling.
   1576         mOnUiThread.setInitialScale(100);
   1577 
   1578         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
   1579         int dimension = Math.max(metrics.widthPixels, metrics.heightPixels);
   1580         // create a paragraph high enough to take up the entire screen
   1581         String p = "<p style=\"height:" + dimension + "px;\">" +
   1582                 "Find all instances of a word on the page and highlight them.</p>";
   1583 
   1584         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p + p + "</body></html>", "text/html", null);
   1585         WaitForFindResultsListener l = new WaitForFindResultsListener(mOnUiThread, "all", 2, true);
   1586         mOnUiThread.setFindListener(l);
   1587 
   1588         // highlight all the strings found and wait for all the matches to be found
   1589         mOnUiThread.findAll("all");
   1590         l.get(MIN_FIND_WAIT_MS, TimeUnit.MILLISECONDS);
   1591         mOnUiThread.setFindListener(null);
   1592 
   1593         int previousScrollY = mOnUiThread.getScrollY();
   1594 
   1595         // Focus "all" in the second page and assert that the view scrolls.
   1596         mOnUiThread.findNext(true);
   1597         waitForScrollingComplete(previousScrollY);
   1598         assertTrue(mOnUiThread.getScrollY() > previousScrollY);
   1599         previousScrollY = mOnUiThread.getScrollY();
   1600 
   1601         // Focus "all" in the first page and assert that the view scrolls.
   1602         mOnUiThread.findNext(true);
   1603         waitForScrollingComplete(previousScrollY);
   1604         assertTrue(mOnUiThread.getScrollY() < previousScrollY);
   1605         previousScrollY = mOnUiThread.getScrollY();
   1606 
   1607         // Focus "all" in the second page and assert that the view scrolls.
   1608         mOnUiThread.findNext(false);
   1609         waitForScrollingComplete(previousScrollY);
   1610         assertTrue(mOnUiThread.getScrollY() > previousScrollY);
   1611         previousScrollY = mOnUiThread.getScrollY();
   1612 
   1613         // Focus "all" in the first page and assert that the view scrolls.
   1614         mOnUiThread.findNext(false);
   1615         waitForScrollingComplete(previousScrollY);
   1616         assertTrue(mOnUiThread.getScrollY() < previousScrollY);
   1617         previousScrollY = mOnUiThread.getScrollY();
   1618 
   1619         // clear the result
   1620         mOnUiThread.clearMatches();
   1621         getInstrumentation().waitForIdleSync();
   1622 
   1623         // can not scroll any more
   1624         mOnUiThread.findNext(false);
   1625         waitForScrollingComplete(previousScrollY);
   1626         assertTrue(mOnUiThread.getScrollY() == previousScrollY);
   1627 
   1628         mOnUiThread.findNext(true);
   1629         waitForScrollingComplete(previousScrollY);
   1630         assertTrue(mOnUiThread.getScrollY() == previousScrollY);
   1631     }
   1632 
   1633     public void testDocumentHasImages() throws Exception, Throwable {
   1634         if (!NullWebViewUtils.isWebViewAvailable()) {
   1635             return;
   1636         }
   1637         final class DocumentHasImageCheckHandler extends Handler {
   1638             private boolean mReceived;
   1639             private int mMsgArg1;
   1640             public DocumentHasImageCheckHandler(Looper looper) {
   1641                 super(looper);
   1642             }
   1643             @Override
   1644             public void handleMessage(Message msg) {
   1645                 synchronized(this) {
   1646                     mReceived = true;
   1647                     mMsgArg1 = msg.arg1;
   1648                 }
   1649             }
   1650             public synchronized boolean hasCalledHandleMessage() {
   1651                 return mReceived;
   1652             }
   1653             public synchronized int getMsgArg1() {
   1654                 return mMsgArg1;
   1655             }
   1656         }
   1657 
   1658         startWebServer(false);
   1659         final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.SMALL_IMG_URL);
   1660 
   1661         // Create a handler on the UI thread.
   1662         final DocumentHasImageCheckHandler handler =
   1663             new DocumentHasImageCheckHandler(mWebView.getHandler().getLooper());
   1664 
   1665         runTestOnUiThread(new Runnable() {
   1666             @Override
   1667             public void run() {
   1668                 mOnUiThread.loadDataAndWaitForCompletion("<html><body><img src=\""
   1669                         + imgUrl + "\"/></body></html>", "text/html", null);
   1670                 Message response = new Message();
   1671                 response.setTarget(handler);
   1672                 assertFalse(handler.hasCalledHandleMessage());
   1673                 mWebView.documentHasImages(response);
   1674             }
   1675         });
   1676         new PollingCheck() {
   1677             @Override
   1678             protected boolean check() {
   1679                 return handler.hasCalledHandleMessage();
   1680             }
   1681         }.run();
   1682         assertEquals(1, handler.getMsgArg1());
   1683     }
   1684 
   1685     private static void waitForFlingDone(WebViewOnUiThread webview) {
   1686         class ScrollDiffPollingCheck extends PollingCheck {
   1687             private static final long TIME_SLICE = 50;
   1688             WebViewOnUiThread mWebView;
   1689             private int mScrollX;
   1690             private int mScrollY;
   1691 
   1692             ScrollDiffPollingCheck(WebViewOnUiThread webview) {
   1693                 mWebView = webview;
   1694                 mScrollX = mWebView.getScrollX();
   1695                 mScrollY = mWebView.getScrollY();
   1696             }
   1697 
   1698             @Override
   1699             protected boolean check() {
   1700                 try {
   1701                     Thread.sleep(TIME_SLICE);
   1702                 } catch (InterruptedException e) {
   1703                     // Intentionally ignored.
   1704                 }
   1705                 int newScrollX = mWebView.getScrollX();
   1706                 int newScrollY = mWebView.getScrollY();
   1707                 boolean flingDone = newScrollX == mScrollX && newScrollY == mScrollY;
   1708                 mScrollX = newScrollX;
   1709                 mScrollY = newScrollY;
   1710                 return flingDone;
   1711             }
   1712         }
   1713         new ScrollDiffPollingCheck(webview).run();
   1714     }
   1715 
   1716     public void testPageScroll() throws Throwable {
   1717         if (!NullWebViewUtils.isWebViewAvailable()) {
   1718             return;
   1719         }
   1720         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
   1721         int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
   1722         String p = "<p style=\"height:" + dimension + "px;\">" +
   1723                 "Scroll by half the size of the page.</p>";
   1724         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1725                 + p + "</body></html>", "text/html", null);
   1726 
   1727         // Wait for UI thread to settle and receive page dimentions from renderer
   1728         // such that we can invoke page down.
   1729         new PollingCheck() {
   1730             @Override
   1731             protected boolean check() {
   1732                  return mOnUiThread.pageDown(false);
   1733             }
   1734         }.run();
   1735 
   1736         do {
   1737             waitForFlingDone(mOnUiThread);
   1738         } while (mOnUiThread.pageDown(false));
   1739 
   1740         waitForFlingDone(mOnUiThread);
   1741         final int bottomScrollY = mOnUiThread.getScrollY();
   1742 
   1743         assertTrue(mOnUiThread.pageUp(false));
   1744 
   1745         do {
   1746             waitForFlingDone(mOnUiThread);
   1747         } while (mOnUiThread.pageUp(false));
   1748 
   1749         waitForFlingDone(mOnUiThread);
   1750         final int topScrollY = mOnUiThread.getScrollY();
   1751 
   1752         // jump to the bottom
   1753         assertTrue(mOnUiThread.pageDown(true));
   1754         new PollingCheck() {
   1755             @Override
   1756             protected boolean check() {
   1757                 return bottomScrollY == mOnUiThread.getScrollY();
   1758             }
   1759         }.run();
   1760 
   1761         // jump to the top
   1762         assertTrue(mOnUiThread.pageUp(true));
   1763          new PollingCheck() {
   1764             @Override
   1765             protected boolean check() {
   1766                 return topScrollY == mOnUiThread.getScrollY();
   1767             }
   1768         }.run();
   1769     }
   1770 
   1771     public void testGetContentHeight() throws Throwable {
   1772         if (!NullWebViewUtils.isWebViewAvailable()) {
   1773             return;
   1774         }
   1775         mOnUiThread.loadDataAndWaitForCompletion(
   1776                 "<html><body></body></html>", "text/html", null);
   1777         new PollingCheck() {
   1778             @Override
   1779             protected boolean check() {
   1780                 return mOnUiThread.getScale() != 0 && mOnUiThread.getContentHeight() != 0
   1781                     && mOnUiThread.getHeight() != 0;
   1782             }
   1783         }.run();
   1784         assertEquals(mOnUiThread.getHeight(),
   1785                 mOnUiThread.getContentHeight() * mOnUiThread.getScale(), 2f);
   1786 
   1787         final int pageHeight = 600;
   1788         // set the margin to 0
   1789         final String p = "<p style=\"height:" + pageHeight
   1790                 + "px;margin:0px auto;\">Get the height of HTML content.</p>";
   1791         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1792                 + "</body></html>", "text/html", null);
   1793         new PollingCheck() {
   1794             @Override
   1795             protected boolean check() {
   1796                 return mOnUiThread.getContentHeight() > pageHeight;
   1797             }
   1798         }.run();
   1799 
   1800         final int extraSpace = mOnUiThread.getContentHeight() - pageHeight;
   1801         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1802                 + p + "</body></html>", "text/html", null);
   1803         new PollingCheck() {
   1804             @Override
   1805             protected boolean check() {
   1806                 return pageHeight + pageHeight + extraSpace == mOnUiThread.getContentHeight();
   1807             }
   1808         }.run();
   1809     }
   1810 
   1811     @UiThreadTest
   1812     public void testPlatformNotifications() {
   1813         if (!NullWebViewUtils.isWebViewAvailable()) {
   1814             return;
   1815         }
   1816         WebView.enablePlatformNotifications();
   1817         WebView.disablePlatformNotifications();
   1818     }
   1819 
   1820     @UiThreadTest
   1821     public void testAccessPluginList() {
   1822         if (!NullWebViewUtils.isWebViewAvailable()) {
   1823             return;
   1824         }
   1825         assertNotNull(WebView.getPluginList());
   1826 
   1827         // can not find a way to install plugins
   1828         mWebView.refreshPlugins(false);
   1829     }
   1830 
   1831     @UiThreadTest
   1832     public void testDestroy() {
   1833         if (!NullWebViewUtils.isWebViewAvailable()) {
   1834             return;
   1835         }
   1836         // Create a new WebView, since we cannot call destroy() on a view in the hierarchy
   1837         WebView localWebView = new WebView(getActivity());
   1838         localWebView.destroy();
   1839     }
   1840 
   1841     public void testFlingScroll() throws Throwable {
   1842         if (!NullWebViewUtils.isWebViewAvailable()) {
   1843             return;
   1844         }
   1845         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
   1846         final int dimension = 10 * Math.max(metrics.widthPixels, metrics.heightPixels);
   1847         String p = "<p style=\"height:" + dimension + "px;" +
   1848                 "width:" + dimension + "px\">Test fling scroll.</p>";
   1849         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1850                 + "</body></html>", "text/html", null);
   1851         new PollingCheck() {
   1852             @Override
   1853             protected boolean check() {
   1854                 return mOnUiThread.getContentHeight() >= dimension;
   1855             }
   1856         }.run();
   1857         getInstrumentation().waitForIdleSync();
   1858 
   1859         final int previousScrollX = mOnUiThread.getScrollX();
   1860         final int previousScrollY = mOnUiThread.getScrollY();
   1861 
   1862         mOnUiThread.flingScroll(100, 100);
   1863 
   1864         new PollingCheck() {
   1865             @Override
   1866             protected boolean check() {
   1867                 return mOnUiThread.getScrollX() > previousScrollX &&
   1868                         mOnUiThread.getScrollY() > previousScrollY;
   1869             }
   1870         }.run();
   1871     }
   1872 
   1873     public void testRequestFocusNodeHref() throws Throwable {
   1874         if (!NullWebViewUtils.isWebViewAvailable()) {
   1875             return;
   1876         }
   1877         startWebServer(false);
   1878         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
   1879         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   1880         final String links = "<DL><p><DT><A HREF=\"" + url1
   1881                 + "\">HTML_URL1</A><DT><A HREF=\"" + url2
   1882                 + "\">HTML_URL2</A></DL><p>";
   1883         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + links + "</body></html>", "text/html", null);
   1884         getInstrumentation().waitForIdleSync();
   1885 
   1886         final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper());
   1887         final Message hrefMsg = new Message();
   1888         hrefMsg.setTarget(handler);
   1889 
   1890         // focus on first link
   1891         handler.reset();
   1892         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
   1893         mOnUiThread.requestFocusNodeHref(hrefMsg);
   1894         new PollingCheck() {
   1895             @Override
   1896             protected boolean check() {
   1897                 boolean done = false;
   1898                 if (handler.hasCalledHandleMessage()) {
   1899                     if (handler.mResultUrl != null) {
   1900                         done = true;
   1901                     } else {
   1902                         handler.reset();
   1903                         Message newMsg = new Message();
   1904                         newMsg.setTarget(handler);
   1905                         mOnUiThread.requestFocusNodeHref(newMsg);
   1906                     }
   1907                 }
   1908                 return done;
   1909             }
   1910         }.run();
   1911         assertEquals(url1, handler.getResultUrl());
   1912 
   1913         // focus on second link
   1914         handler.reset();
   1915         final Message hrefMsg2 = new Message();
   1916         hrefMsg2.setTarget(handler);
   1917         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
   1918         mOnUiThread.requestFocusNodeHref(hrefMsg2);
   1919         new PollingCheck() {
   1920             @Override
   1921             protected boolean check() {
   1922                 boolean done = false;
   1923                 final String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   1924                 if (handler.hasCalledHandleMessage()) {
   1925                     if (handler.mResultUrl != null &&
   1926                             handler.mResultUrl.equals(url2)) {
   1927                         done = true;
   1928                     } else {
   1929                         handler.reset();
   1930                         Message newMsg = new Message();
   1931                         newMsg.setTarget(handler);
   1932                         mOnUiThread.requestFocusNodeHref(newMsg);
   1933                     }
   1934                 }
   1935                 return done;
   1936             }
   1937         }.run();
   1938         assertEquals(url2, handler.getResultUrl());
   1939 
   1940         mOnUiThread.requestFocusNodeHref(null);
   1941     }
   1942 
   1943     public void testRequestImageRef() throws Exception, Throwable {
   1944         if (!NullWebViewUtils.isWebViewAvailable()) {
   1945             return;
   1946         }
   1947         final class ImageLoaded {
   1948             public boolean mImageLoaded;
   1949 
   1950             @JavascriptInterface
   1951             public void loaded() {
   1952                 mImageLoaded = true;
   1953             }
   1954         }
   1955         final ImageLoaded imageLoaded = new ImageLoaded();
   1956         runTestOnUiThread(new Runnable() {
   1957             public void run() {
   1958                 mOnUiThread.getSettings().setJavaScriptEnabled(true);
   1959             }
   1960         });
   1961         mOnUiThread.addJavascriptInterface(imageLoaded, "imageLoaded");
   1962         AssetManager assets = getActivity().getAssets();
   1963         Bitmap bitmap = BitmapFactory.decodeStream(assets.open(TestHtmlConstants.LARGE_IMG_URL));
   1964         int imgWidth = bitmap.getWidth();
   1965         int imgHeight = bitmap.getHeight();
   1966 
   1967         startWebServer(false);
   1968         final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.LARGE_IMG_URL);
   1969         mOnUiThread.loadDataAndWaitForCompletion(
   1970                 "<html><head><title>Title</title><style type=\"text/css\">"
   1971                 + "#imgElement { -webkit-transform: translate3d(0,0,1); }"
   1972                 + "#imgElement.finish { -webkit-transform: translate3d(0,0,0);"
   1973                 + " -webkit-transition-duration: 1ms; }</style>"
   1974                 + "<script type=\"text/javascript\">function imgLoad() {"
   1975                 + "imgElement = document.getElementById('imgElement');"
   1976                 + "imgElement.addEventListener('webkitTransitionEnd',"
   1977                 + "function(e) { imageLoaded.loaded(); });"
   1978                 + "imgElement.className = 'finish';}</script>"
   1979                 + "</head><body><img id=\"imgElement\" src=\"" + imgUrl
   1980                 + "\" width=\"" + imgWidth + "\" height=\"" + imgHeight
   1981                 + "\" onLoad=\"imgLoad()\"/></body></html>", "text/html", null);
   1982         new PollingCheck() {
   1983             @Override
   1984             protected boolean check() {
   1985                 return imageLoaded.mImageLoaded;
   1986             }
   1987         }.run();
   1988         getInstrumentation().waitForIdleSync();
   1989 
   1990         final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper());
   1991         final Message msg = new Message();
   1992         msg.setTarget(handler);
   1993 
   1994         // touch the image
   1995         handler.reset();
   1996         int[] location = mOnUiThread.getLocationOnScreen();
   1997 
   1998         long time = SystemClock.uptimeMillis();
   1999         getInstrumentation().sendPointerSync(
   2000                 MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN,
   2001                         location[0] + imgWidth / 2,
   2002                         location[1] + imgHeight / 2, 0));
   2003         getInstrumentation().waitForIdleSync();
   2004         mOnUiThread.requestImageRef(msg);
   2005         new PollingCheck() {
   2006             @Override
   2007             protected boolean check() {
   2008                 boolean done = false;
   2009                 if (handler.hasCalledHandleMessage()) {
   2010                     if (handler.mResultUrl != null) {
   2011                         done = true;
   2012                     } else {
   2013                         handler.reset();
   2014                         Message newMsg = new Message();
   2015                         newMsg.setTarget(handler);
   2016                         mOnUiThread.requestImageRef(newMsg);
   2017                     }
   2018                 }
   2019                 return done;
   2020             }
   2021         }.run();
   2022         assertEquals(imgUrl, handler.mResultUrl);
   2023     }
   2024 
   2025     @UiThreadTest
   2026     public void testDebugDump() {
   2027         if (!NullWebViewUtils.isWebViewAvailable()) {
   2028             return;
   2029         }
   2030         mWebView.debugDump();
   2031     }
   2032 
   2033     public void testGetHitTestResult() throws Throwable {
   2034         if (!NullWebViewUtils.isWebViewAvailable()) {
   2035             return;
   2036         }
   2037         final String anchor = "<p><a href=\"" + TestHtmlConstants.EXT_WEB_URL1
   2038                 + "\">normal anchor</a></p>";
   2039         final String blankAnchor = "<p><a href=\"\">blank anchor</a></p>";
   2040         final String form = "<p><form><input type=\"text\" name=\"Test\"><br>"
   2041                 + "<input type=\"submit\" value=\"Submit\"></form></p>";
   2042         String phoneNo = "3106984000";
   2043         final String tel = "<p><a href=\"tel:" + phoneNo + "\">Phone</a></p>";
   2044         String email = "test (at) gmail.com";
   2045         final String mailto = "<p><a href=\"mailto:" + email + "\">Email</a></p>";
   2046         String location = "shanghai";
   2047         final String geo = "<p><a href=\"geo:0,0?q=" + location + "\">Location</a></p>";
   2048 
   2049         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("fake://home",
   2050                 "<html><body>" + anchor + blankAnchor + form + tel + mailto +
   2051                 geo + "</body></html>", "text/html", "UTF-8", null);
   2052         getInstrumentation().waitForIdleSync();
   2053 
   2054         // anchor
   2055         moveFocusDown();
   2056         HitTestResult hitTestResult = mOnUiThread.getHitTestResult();
   2057         assertEquals(HitTestResult.SRC_ANCHOR_TYPE, hitTestResult.getType());
   2058         assertEquals(TestHtmlConstants.EXT_WEB_URL1, hitTestResult.getExtra());
   2059 
   2060         // blank anchor
   2061         moveFocusDown();
   2062         hitTestResult = mOnUiThread.getHitTestResult();
   2063         assertEquals(HitTestResult.SRC_ANCHOR_TYPE, hitTestResult.getType());
   2064         assertEquals("fake://home", hitTestResult.getExtra());
   2065 
   2066         // text field
   2067         moveFocusDown();
   2068         hitTestResult = mOnUiThread.getHitTestResult();
   2069         assertEquals(HitTestResult.EDIT_TEXT_TYPE, hitTestResult.getType());
   2070         assertNull(hitTestResult.getExtra());
   2071 
   2072         // submit button
   2073         moveFocusDown();
   2074         hitTestResult = mOnUiThread.getHitTestResult();
   2075         assertEquals(HitTestResult.UNKNOWN_TYPE, hitTestResult.getType());
   2076         assertNull(hitTestResult.getExtra());
   2077 
   2078         // phone number
   2079         moveFocusDown();
   2080         hitTestResult = mOnUiThread.getHitTestResult();
   2081         assertEquals(HitTestResult.PHONE_TYPE, hitTestResult.getType());
   2082         assertEquals(phoneNo, hitTestResult.getExtra());
   2083 
   2084         // email
   2085         moveFocusDown();
   2086         hitTestResult = mOnUiThread.getHitTestResult();
   2087         assertEquals(HitTestResult.EMAIL_TYPE, hitTestResult.getType());
   2088         assertEquals(email, hitTestResult.getExtra());
   2089 
   2090         // geo address
   2091         moveFocusDown();
   2092         hitTestResult = mOnUiThread.getHitTestResult();
   2093         assertEquals(HitTestResult.GEO_TYPE, hitTestResult.getType());
   2094         assertEquals(location, hitTestResult.getExtra());
   2095     }
   2096 
   2097     public void testSetInitialScale() throws Throwable {
   2098         if (!NullWebViewUtils.isWebViewAvailable()) {
   2099             return;
   2100         }
   2101         final String p = "<p style=\"height:1000px;width:1000px\">Test setInitialScale.</p>";
   2102         final float defaultScale =
   2103             getActivity().getResources().getDisplayMetrics().density;
   2104 
   2105         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   2106                 + "</body></html>", "text/html", null);
   2107 
   2108         new PollingCheck(TEST_TIMEOUT) {
   2109             @Override
   2110             protected boolean check() {
   2111                 return Math.abs(defaultScale - mOnUiThread.getScale()) < .01f;
   2112             }
   2113         }.run();
   2114 
   2115         mOnUiThread.setInitialScale(0);
   2116         // modify content to fool WebKit into re-loading
   2117         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   2118                 + "2" + "</body></html>", "text/html", null);
   2119 
   2120         new PollingCheck(TEST_TIMEOUT) {
   2121             @Override
   2122             protected boolean check() {
   2123                 return Math.abs(defaultScale - mOnUiThread.getScale()) < .01f;
   2124             }
   2125         }.run();
   2126 
   2127         mOnUiThread.setInitialScale(50);
   2128         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   2129                 + "3" + "</body></html>", "text/html", null);
   2130 
   2131         new PollingCheck(TEST_TIMEOUT) {
   2132             @Override
   2133             protected boolean check() {
   2134                 return Math.abs(0.5 - mOnUiThread.getScale()) < .01f;
   2135             }
   2136         }.run();
   2137 
   2138         mOnUiThread.setInitialScale(0);
   2139         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   2140                 + "4" + "</body></html>", "text/html", null);
   2141 
   2142         new PollingCheck(TEST_TIMEOUT) {
   2143             @Override
   2144             protected boolean check() {
   2145                 return Math.abs(defaultScale - mOnUiThread.getScale()) < .01f;
   2146             }
   2147         }.run();
   2148     }
   2149 
   2150     @UiThreadTest
   2151     public void testClearHistory() throws Exception {
   2152         if (!NullWebViewUtils.isWebViewAvailable()) {
   2153             return;
   2154         }
   2155         startWebServer(false);
   2156         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
   2157         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   2158         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
   2159 
   2160         mOnUiThread.loadUrlAndWaitForCompletion(url1);
   2161         pollingCheckWebBackForwardList(url1, 0, 1);
   2162 
   2163         mOnUiThread.loadUrlAndWaitForCompletion(url2);
   2164         pollingCheckWebBackForwardList(url2, 1, 2);
   2165 
   2166         mOnUiThread.loadUrlAndWaitForCompletion(url3);
   2167         pollingCheckWebBackForwardList(url3, 2, 3);
   2168 
   2169         mWebView.clearHistory();
   2170 
   2171         // only current URL is left after clearing
   2172         pollingCheckWebBackForwardList(url3, 0, 1);
   2173     }
   2174 
   2175     @UiThreadTest
   2176     public void testSaveAndRestoreState() throws Throwable {
   2177         if (!NullWebViewUtils.isWebViewAvailable()) {
   2178             return;
   2179         }
   2180         // nothing to save
   2181         assertNull(mWebView.saveState(new Bundle()));
   2182 
   2183         startWebServer(false);
   2184         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
   2185         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   2186         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
   2187 
   2188         // make a history list
   2189         mOnUiThread.loadUrlAndWaitForCompletion(url1);
   2190         pollingCheckWebBackForwardList(url1, 0, 1);
   2191         mOnUiThread.loadUrlAndWaitForCompletion(url2);
   2192         pollingCheckWebBackForwardList(url2, 1, 2);
   2193         mOnUiThread.loadUrlAndWaitForCompletion(url3);
   2194         pollingCheckWebBackForwardList(url3, 2, 3);
   2195 
   2196         // save the list
   2197         Bundle bundle = new Bundle();
   2198         WebBackForwardList saveList = mWebView.saveState(bundle);
   2199         assertNotNull(saveList);
   2200         assertEquals(3, saveList.getSize());
   2201         assertEquals(2, saveList.getCurrentIndex());
   2202         assertEquals(url1, saveList.getItemAtIndex(0).getUrl());
   2203         assertEquals(url2, saveList.getItemAtIndex(1).getUrl());
   2204         assertEquals(url3, saveList.getItemAtIndex(2).getUrl());
   2205 
   2206         // change the content to a new "blank" web view without history
   2207         final WebView newWebView = new WebView(getActivity());
   2208 
   2209         WebBackForwardList copyListBeforeRestore = newWebView.copyBackForwardList();
   2210         assertNotNull(copyListBeforeRestore);
   2211         assertEquals(0, copyListBeforeRestore.getSize());
   2212 
   2213         // restore the list
   2214         final WebBackForwardList restoreList = newWebView.restoreState(bundle);
   2215         assertNotNull(restoreList);
   2216         assertEquals(3, restoreList.getSize());
   2217         assertEquals(2, saveList.getCurrentIndex());
   2218 
   2219         // wait for the list items to get inflated
   2220         new PollingCheck(TEST_TIMEOUT) {
   2221             @Override
   2222             protected boolean check() {
   2223                 return restoreList.getItemAtIndex(0).getUrl() != null &&
   2224                        restoreList.getItemAtIndex(1).getUrl() != null &&
   2225                        restoreList.getItemAtIndex(2).getUrl() != null;
   2226             }
   2227         }.run();
   2228         assertEquals(url1, restoreList.getItemAtIndex(0).getUrl());
   2229         assertEquals(url2, restoreList.getItemAtIndex(1).getUrl());
   2230         assertEquals(url3, restoreList.getItemAtIndex(2).getUrl());
   2231 
   2232         WebBackForwardList copyListAfterRestore = newWebView.copyBackForwardList();
   2233         assertNotNull(copyListAfterRestore);
   2234         assertEquals(3, copyListAfterRestore.getSize());
   2235         assertEquals(2, copyListAfterRestore.getCurrentIndex());
   2236         assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl());
   2237         assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl());
   2238         assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl());
   2239     }
   2240 
   2241     public void testSetWebViewClient() throws Throwable {
   2242         if (!NullWebViewUtils.isWebViewAvailable()) {
   2243             return;
   2244         }
   2245         final ScaleChangedWebViewClient webViewClient = new ScaleChangedWebViewClient();
   2246         mOnUiThread.setWebViewClient(webViewClient);
   2247         startWebServer(false);
   2248 
   2249         assertFalse(webViewClient.onScaleChangedCalled());
   2250         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   2251         mOnUiThread.loadUrlAndWaitForCompletion(url1);
   2252         pollingCheckForCanZoomIn();
   2253 
   2254         assertTrue(mOnUiThread.zoomIn());
   2255         webViewClient.waitForScaleChanged();
   2256     }
   2257 
   2258     public void testRequestChildRectangleOnScreen() throws Throwable {
   2259         if (!NullWebViewUtils.isWebViewAvailable()) {
   2260             return;
   2261         }
   2262 
   2263         // It is needed to make test pass on some devices.
   2264         mOnUiThread.setLayoutToMatchParent();
   2265 
   2266         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
   2267         final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
   2268         String p = "<p style=\"height:" + dimension + "px;width:" + dimension + "px\">&nbsp;</p>";
   2269         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   2270                 + "</body></html>", "text/html", null);
   2271         new PollingCheck() {
   2272             @Override
   2273             protected boolean check() {
   2274                 return mOnUiThread.getContentHeight() >= dimension;
   2275             }
   2276         }.run();
   2277 
   2278         int origX = mOnUiThread.getScrollX();
   2279         int origY = mOnUiThread.getScrollY();
   2280 
   2281         int half = dimension / 2;
   2282         Rect rect = new Rect(half, half, half + 1, half + 1);
   2283         assertTrue(mOnUiThread.requestChildRectangleOnScreen(mWebView, rect, true));
   2284         assertTrue(mOnUiThread.getScrollX() > origX);
   2285         assertTrue(mOnUiThread.getScrollY() > origY);
   2286     }
   2287 
   2288     public void testSetDownloadListener() throws Throwable {
   2289         if (!NullWebViewUtils.isWebViewAvailable()) {
   2290             return;
   2291         }
   2292 
   2293         final CountDownLatch resultLatch = new CountDownLatch(1);
   2294         final class MyDownloadListener implements DownloadListener {
   2295             public String url;
   2296             public String mimeType;
   2297             public long contentLength;
   2298             public String contentDisposition;
   2299 
   2300             @Override
   2301             public void onDownloadStart(String url, String userAgent, String contentDisposition,
   2302                     String mimetype, long contentLength) {
   2303                 this.url = url;
   2304                 this.mimeType = mimetype;
   2305                 this.contentLength = contentLength;
   2306                 this.contentDisposition = contentDisposition;
   2307                 resultLatch.countDown();
   2308             }
   2309         }
   2310 
   2311         final String mimeType = "application/octet-stream";
   2312         final int length = 100;
   2313         final MyDownloadListener listener = new MyDownloadListener();
   2314 
   2315         startWebServer(false);
   2316         final String url = mWebServer.getBinaryUrl(mimeType, length);
   2317 
   2318         // By default, WebView sends an intent to ask the system to
   2319         // handle loading a new URL. We set WebViewClient as
   2320         // WebViewClient.shouldOverrideUrlLoading() returns false, so
   2321         // the WebView will load the new URL.
   2322         mOnUiThread.setDownloadListener(listener);
   2323         mOnUiThread.getSettings().setJavaScriptEnabled(true);
   2324         mOnUiThread.loadDataAndWaitForCompletion(
   2325                 "<html><body onload=\"window.location = \'" + url + "\'\"></body></html>",
   2326                 "text/html", null);
   2327         // Wait for layout to complete before setting focus.
   2328         getInstrumentation().waitForIdleSync();
   2329 
   2330         assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   2331         assertEquals(url, listener.url);
   2332         assertTrue(listener.contentDisposition.contains("test.bin"));
   2333         assertEquals(length, listener.contentLength);
   2334         assertEquals(mimeType, listener.mimeType);
   2335     }
   2336 
   2337     @UiThreadTest
   2338     public void testSetLayoutParams() {
   2339         if (!NullWebViewUtils.isWebViewAvailable()) {
   2340             return;
   2341         }
   2342         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(600, 800);
   2343         mWebView.setLayoutParams(params);
   2344         assertSame(params, mWebView.getLayoutParams());
   2345     }
   2346 
   2347     @UiThreadTest
   2348     public void testSetMapTrackballToArrowKeys() {
   2349         if (!NullWebViewUtils.isWebViewAvailable()) {
   2350             return;
   2351         }
   2352         mWebView.setMapTrackballToArrowKeys(true);
   2353     }
   2354 
   2355     public void testSetNetworkAvailable() throws Exception {
   2356         if (!NullWebViewUtils.isWebViewAvailable()) {
   2357             return;
   2358         }
   2359         WebSettings settings = mOnUiThread.getSettings();
   2360         settings.setJavaScriptEnabled(true);
   2361         startWebServer(false);
   2362 
   2363         String url = mWebServer.getAssetUrl(TestHtmlConstants.NETWORK_STATE_URL);
   2364         mOnUiThread.loadUrlAndWaitForCompletion(url);
   2365         assertEquals("ONLINE", mOnUiThread.getTitle());
   2366 
   2367         mOnUiThread.setNetworkAvailable(false);
   2368 
   2369         // Wait for the DOM to receive notification of the network state change.
   2370         new PollingCheck(TEST_TIMEOUT) {
   2371             @Override
   2372             protected boolean check() {
   2373                 return mOnUiThread.getTitle().equals("OFFLINE");
   2374             }
   2375         }.run();
   2376 
   2377         mOnUiThread.setNetworkAvailable(true);
   2378 
   2379         // Wait for the DOM to receive notification of the network state change.
   2380         new PollingCheck(TEST_TIMEOUT) {
   2381             @Override
   2382             protected boolean check() {
   2383                 return mOnUiThread.getTitle().equals("ONLINE");
   2384             }
   2385         }.run();
   2386     }
   2387 
   2388     public void testSetWebChromeClient() throws Throwable {
   2389         if (!NullWebViewUtils.isWebViewAvailable()) {
   2390             return;
   2391         }
   2392         final class MockWebChromeClient extends WaitForProgressClient {
   2393             private boolean mOnProgressChanged = false;
   2394 
   2395             public MockWebChromeClient() {
   2396                 super(mOnUiThread);
   2397             }
   2398 
   2399             @Override
   2400             public void onProgressChanged(WebView view, int newProgress) {
   2401                 super.onProgressChanged(view, newProgress);
   2402                 mOnProgressChanged = true;
   2403             }
   2404             public boolean onProgressChangedCalled() {
   2405                 return mOnProgressChanged;
   2406             }
   2407         }
   2408 
   2409         final MockWebChromeClient webChromeClient = new MockWebChromeClient();
   2410 
   2411         mOnUiThread.setWebChromeClient(webChromeClient);
   2412         getInstrumentation().waitForIdleSync();
   2413         assertFalse(webChromeClient.onProgressChangedCalled());
   2414 
   2415         startWebServer(false);
   2416         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   2417         mOnUiThread.loadUrlAndWaitForCompletion(url);
   2418         getInstrumentation().waitForIdleSync();
   2419 
   2420         new PollingCheck(TEST_TIMEOUT) {
   2421             @Override
   2422             protected boolean check() {
   2423                 return webChromeClient.onProgressChangedCalled();
   2424             }
   2425         }.run();
   2426     }
   2427 
   2428     public void testPauseResumeTimers() throws Throwable {
   2429         if (!NullWebViewUtils.isWebViewAvailable()) {
   2430             return;
   2431         }
   2432         class Monitor {
   2433             private boolean mIsUpdated;
   2434 
   2435             @JavascriptInterface
   2436             public synchronized void update() {
   2437                 mIsUpdated  = true;
   2438                 notify();
   2439             }
   2440             public synchronized boolean waitForUpdate() {
   2441                 while (!mIsUpdated) {
   2442                     try {
   2443                         // This is slightly flaky, as we can't guarantee that
   2444                         // this is a sufficient time limit, but there's no way
   2445                         // around this.
   2446                         wait(1000);
   2447                         if (!mIsUpdated) {
   2448                             return false;
   2449                         }
   2450                     } catch (InterruptedException e) {
   2451                     }
   2452                 }
   2453                 mIsUpdated = false;
   2454                 return true;
   2455             }
   2456         };
   2457         final Monitor monitor = new Monitor();
   2458         final String updateMonitorHtml = "<html>" +
   2459                 "<body onload=\"monitor.update();\"></body></html>";
   2460 
   2461         // Test that JavaScript is executed even with timers paused.
   2462         runTestOnUiThread(new Runnable() {
   2463             @Override
   2464             public void run() {
   2465                 mWebView.getSettings().setJavaScriptEnabled(true);
   2466                 mWebView.addJavascriptInterface(monitor, "monitor");
   2467                 mWebView.pauseTimers();
   2468                 mOnUiThread.loadDataAndWaitForCompletion(updateMonitorHtml,
   2469                         "text/html", null);
   2470             }
   2471         });
   2472         assertTrue(monitor.waitForUpdate());
   2473 
   2474         // Start a timer and test that it does not fire.
   2475         mOnUiThread.loadDataAndWaitForCompletion(
   2476                 "<html><body onload='setTimeout(function(){monitor.update();},100)'>" +
   2477                 "</body></html>", "text/html", null);
   2478         assertFalse(monitor.waitForUpdate());
   2479 
   2480         // Resume timers and test that the timer fires.
   2481         mOnUiThread.resumeTimers();
   2482         assertTrue(monitor.waitForUpdate());
   2483     }
   2484 
   2485     // verify query parameters can be passed correctly to android asset files
   2486     public void testAndroidAssetQueryParam() {
   2487         if (!NullWebViewUtils.isWebViewAvailable()) {
   2488             return;
   2489         }
   2490 
   2491         WebSettings settings = mOnUiThread.getSettings();
   2492         settings.setJavaScriptEnabled(true);
   2493         // test passing a parameter
   2494         String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.PARAM_ASSET_URL+"?val=SUCCESS");
   2495         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
   2496         assertEquals("SUCCESS", mOnUiThread.getTitle());
   2497     }
   2498 
   2499     // verify anchors work correctly for android asset files
   2500     public void testAndroidAssetAnchor() {
   2501         if (!NullWebViewUtils.isWebViewAvailable()) {
   2502             return;
   2503         }
   2504 
   2505         WebSettings settings = mOnUiThread.getSettings();
   2506         settings.setJavaScriptEnabled(true);
   2507         // test using an anchor
   2508         String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.ANCHOR_ASSET_URL+"#anchor");
   2509         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
   2510         assertEquals("anchor", mOnUiThread.getTitle());
   2511     }
   2512 
   2513     public void testEvaluateJavascript() {
   2514         if (!NullWebViewUtils.isWebViewAvailable()) {
   2515             return;
   2516         }
   2517         mOnUiThread.getSettings().setJavaScriptEnabled(true);
   2518         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
   2519 
   2520         EvaluateJsResultPollingCheck jsResult = new EvaluateJsResultPollingCheck("2");
   2521         mOnUiThread.evaluateJavascript("1+1", jsResult);
   2522         jsResult.run();
   2523 
   2524         jsResult = new EvaluateJsResultPollingCheck("9");
   2525         mOnUiThread.evaluateJavascript("1+1; 4+5", jsResult);
   2526         jsResult.run();
   2527 
   2528         final String EXPECTED_TITLE = "test";
   2529         mOnUiThread.evaluateJavascript("document.title='" + EXPECTED_TITLE + "';", null);
   2530         new PollingCheck(TEST_TIMEOUT) {
   2531             @Override
   2532             protected boolean check() {
   2533                 return mOnUiThread.getTitle().equals(EXPECTED_TITLE);
   2534             }
   2535         }.run();
   2536     }
   2537 
   2538     // Verify Print feature can create a PDF file with a correct preamble.
   2539     public void testPrinting() throws Throwable {
   2540         if (!NullWebViewUtils.isWebViewAvailable()) {
   2541             return;
   2542         }
   2543         mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" +
   2544                 "<body>foo</body></html>",
   2545                 "text/html", null);
   2546         final PrintDocumentAdapter adapter =  mOnUiThread.createPrintDocumentAdapter();
   2547         printDocumentStart(adapter);
   2548         PrintAttributes attributes = new PrintAttributes.Builder()
   2549                 .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
   2550                 .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
   2551                 .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
   2552                 .build();
   2553         final WebViewCtsActivity activity = getActivity();
   2554         final File file = activity.getFileStreamPath(PRINTER_TEST_FILE);
   2555         final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file,
   2556                 ParcelFileDescriptor.parseMode("w"));
   2557         final FutureTask<Boolean> result =
   2558                 new FutureTask<Boolean>(new Callable<Boolean>() {
   2559                             public Boolean call() {
   2560                                 return true;
   2561                             }
   2562                         });
   2563         printDocumentLayout(adapter, null, attributes,
   2564                 new LayoutResultCallback() {
   2565                     // Called on UI thread
   2566                     @Override
   2567                     public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
   2568                         PageRange[] pageRanges = new PageRange[] {PageRange.ALL_PAGES};
   2569                         savePrintedPage(adapter, descriptor, pageRanges, result);
   2570                     }
   2571                 });
   2572         try {
   2573             result.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
   2574             assertTrue(file.length() > 0);
   2575             FileInputStream in = new FileInputStream(file);
   2576             byte[] b = new byte[PDF_PREAMBLE.length()];
   2577             in.read(b);
   2578             String preamble = new String(b);
   2579             assertEquals(PDF_PREAMBLE, preamble);
   2580         } finally {
   2581             // close the descriptor, if not closed already.
   2582             descriptor.close();
   2583             file.delete();
   2584         }
   2585     }
   2586 
   2587     // Verify Print feature can create a PDF file with correct number of pages.
   2588     public void testPrintingPagesCount() throws Throwable {
   2589         if (!NullWebViewUtils.isWebViewAvailable()) {
   2590             return;
   2591         }
   2592         String content = "<html><head></head><body>";
   2593         for (int i = 0; i < 500; ++i) {
   2594             content += "<br />abcdefghijk<br />";
   2595         }
   2596         content += "</body></html>";
   2597         mOnUiThread.loadDataAndWaitForCompletion(content, "text/html", null);
   2598         final PrintDocumentAdapter adapter =  mOnUiThread.createPrintDocumentAdapter();
   2599         printDocumentStart(adapter);
   2600         PrintAttributes attributes = new PrintAttributes.Builder()
   2601                 .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
   2602                 .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
   2603                 .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
   2604                 .build();
   2605         final WebViewCtsActivity activity = getActivity();
   2606         final File file = activity.getFileStreamPath(PRINTER_TEST_FILE);
   2607         final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file,
   2608                 ParcelFileDescriptor.parseMode("w"));
   2609         final FutureTask<Boolean> result =
   2610                 new FutureTask<Boolean>(new Callable<Boolean>() {
   2611                             public Boolean call() {
   2612                                 return true;
   2613                             }
   2614                         });
   2615         printDocumentLayout(adapter, null, attributes,
   2616                 new LayoutResultCallback() {
   2617                     // Called on UI thread
   2618                     @Override
   2619                     public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
   2620                         PageRange[] pageRanges = new PageRange[] {
   2621                             new PageRange(1, 1), new PageRange(4, 7)
   2622                         };
   2623                         savePrintedPage(adapter, descriptor, pageRanges, result);
   2624                     }
   2625                 });
   2626         try {
   2627             result.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
   2628             assertTrue(file.length() > 0);
   2629             PdfRenderer renderer = new PdfRenderer(
   2630                 ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
   2631             assertEquals(5, renderer.getPageCount());
   2632         } finally {
   2633             descriptor.close();
   2634             file.delete();
   2635         }
   2636     }
   2637 
   2638     public void testVisualStateCallbackCalled() throws Exception {
   2639         // Check that the visual state callback is called correctly.
   2640         if (!NullWebViewUtils.isWebViewAvailable()) {
   2641             return;
   2642         }
   2643 
   2644         final CountDownLatch callbackLatch = new CountDownLatch(1);
   2645         final long kRequest = 100;
   2646 
   2647         mOnUiThread.loadUrl("about:blank");
   2648 
   2649         mOnUiThread.postVisualStateCallback(kRequest, new VisualStateCallback() {
   2650             public void onComplete(long requestId) {
   2651                 assertEquals(kRequest, requestId);
   2652                 callbackLatch.countDown();
   2653             }
   2654         });
   2655 
   2656         assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   2657     }
   2658 
   2659     public void testOnPageCommitVisibleCalled() throws Exception {
   2660         // Check that the onPageCommitVisible callback is called
   2661         // correctly.
   2662         if (!NullWebViewUtils.isWebViewAvailable()) {
   2663             return;
   2664         }
   2665 
   2666         final CountDownLatch callbackLatch = new CountDownLatch(1);
   2667 
   2668         mOnUiThread.setWebViewClient(new WebViewClient() {
   2669                 public void onPageCommitVisible(WebView view, String url) {
   2670                     assertEquals(url, "about:blank");
   2671                     callbackLatch.countDown();
   2672                 }
   2673             });
   2674 
   2675         mOnUiThread.loadUrl("about:blank");
   2676         assertTrue(callbackLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   2677     }
   2678 
   2679     public void testSetSafeBrowsingWhitelistWithMalformedList() throws Exception {
   2680         if (!NullWebViewUtils.isWebViewAvailable()) {
   2681             return;
   2682         }
   2683 
   2684         List whitelist = new ArrayList<String>();
   2685         // Protocols are not supported in the whitelist
   2686         whitelist.add("http://google.com");
   2687         final CountDownLatch resultLatch = new CountDownLatch(1);
   2688         WebView.setSafeBrowsingWhitelist(whitelist, new ValueCallback<Boolean>() {
   2689             @Override
   2690             public void onReceiveValue(Boolean success) {
   2691                 assertFalse(success);
   2692                 resultLatch.countDown();
   2693             }
   2694         });
   2695         assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   2696     }
   2697 
   2698     public void testSetSafeBrowsingWhitelistWithValidList() throws Exception {
   2699         if (!NullWebViewUtils.isWebViewAvailable()) {
   2700             return;
   2701         }
   2702 
   2703         List whitelist = new ArrayList<String>();
   2704         whitelist.add("safe-browsing");
   2705         final CountDownLatch resultLatch = new CountDownLatch(1);
   2706         WebView.setSafeBrowsingWhitelist(whitelist, new ValueCallback<Boolean>() {
   2707             @Override
   2708             public void onReceiveValue(Boolean success) {
   2709                 assertTrue(success);
   2710                 resultLatch.countDown();
   2711             }
   2712         });
   2713         assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   2714 
   2715         final CountDownLatch resultLatch2 = new CountDownLatch(1);
   2716         mOnUiThread.setWebViewClient(new WebViewClient() {
   2717             @Override
   2718             public void onPageFinished(WebView view, String url) {
   2719                 resultLatch2.countDown();
   2720             }
   2721 
   2722             @Override
   2723             public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType,
   2724                     SafeBrowsingResponse callback) {
   2725                 Assert.fail("Should not invoke onSafeBrowsingHit");
   2726             }
   2727         });
   2728 
   2729         mOnUiThread.loadUrl("chrome://safe-browsing/match?type=malware");
   2730 
   2731         // Wait until page load has completed
   2732         assertTrue(resultLatch2.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   2733     }
   2734 
   2735     @UiThreadTest
   2736     public void testGetWebViewClient() throws Exception {
   2737         if (!NullWebViewUtils.isWebViewAvailable()) {
   2738             return;
   2739         }
   2740 
   2741         // getWebViewClient should return a default WebViewClient if it hasn't been set yet
   2742         WebView webView = new WebView(getActivity());
   2743         WebViewClient client = webView.getWebViewClient();
   2744         assertNotNull(client);
   2745         assertTrue(client instanceof WebViewClient);
   2746 
   2747         // getWebViewClient should return the client after it has been set
   2748         WebViewClient client2 = new WebViewClient();
   2749         assertNotSame(client, client2);
   2750         webView.setWebViewClient(client2);
   2751         assertSame(client2, webView.getWebViewClient());
   2752     }
   2753 
   2754     @UiThreadTest
   2755     public void testGetWebChromeClient() throws Exception {
   2756         if (!NullWebViewUtils.isWebViewAvailable()) {
   2757             return;
   2758         }
   2759 
   2760         // getWebChromeClient should return null if the client hasn't been set yet
   2761         WebView webView = new WebView(getActivity());
   2762         WebChromeClient client = webView.getWebChromeClient();
   2763         assertNull(client);
   2764 
   2765         // getWebChromeClient should return the client after it has been set
   2766         WebChromeClient client2 = new WebChromeClient();
   2767         assertNotSame(client, client2);
   2768         webView.setWebChromeClient(client2);
   2769         assertSame(client2, webView.getWebChromeClient());
   2770     }
   2771 
   2772     @UiThreadTest
   2773     public void testSetCustomTextClassifier() throws Exception {
   2774         if (!NullWebViewUtils.isWebViewAvailable()) {
   2775             return;
   2776         }
   2777 
   2778         class CustomTextClassifier implements TextClassifier {
   2779             @Override
   2780             public TextSelection suggestSelection(
   2781                 CharSequence text,
   2782                 int startIndex,
   2783                 int endIndex,
   2784                 LocaleList defaultLocales) {
   2785                 return new TextSelection.Builder(0, 1).build();
   2786             }
   2787 
   2788             @Override
   2789             public TextClassification classifyText(
   2790                 CharSequence text,
   2791                 int startIndex,
   2792                 int endIndex,
   2793                 LocaleList defaultLocales) {
   2794                 return new TextClassification.Builder().build();
   2795             }
   2796         };
   2797 
   2798         TextClassifier classifier = new CustomTextClassifier();
   2799         WebView webView = new WebView(getActivity());
   2800         webView.setTextClassifier(classifier);
   2801         assertSame(webView.getTextClassifier(), classifier);
   2802     }
   2803 
   2804     private static class MockContext extends ContextWrapper {
   2805         private boolean mGetApplicationContextWasCalled;
   2806 
   2807         public MockContext(Context context) {
   2808             super(context);
   2809         }
   2810 
   2811         public Context getApplicationContext() {
   2812             mGetApplicationContextWasCalled = true;
   2813             return super.getApplicationContext();
   2814         }
   2815 
   2816         public boolean wasGetApplicationContextCalled() {
   2817             return mGetApplicationContextWasCalled;
   2818         }
   2819     }
   2820 
   2821     public void testStartSafeBrowsingUseApplicationContext() throws Exception {
   2822         if (!NullWebViewUtils.isWebViewAvailable()) {
   2823             return;
   2824         }
   2825 
   2826         final MockContext ctx = new MockContext(getActivity());
   2827         final CountDownLatch resultLatch = new CountDownLatch(1);
   2828         WebView.startSafeBrowsing(ctx, new ValueCallback<Boolean>() {
   2829             @Override
   2830             public void onReceiveValue(Boolean value) {
   2831                 assertTrue(ctx.wasGetApplicationContextCalled());
   2832                 resultLatch.countDown();
   2833                 return;
   2834             }
   2835         });
   2836         assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   2837     }
   2838 
   2839     public void testStartSafeBrowsingWithNullCallbackDoesntCrash() throws Exception {
   2840         if (!NullWebViewUtils.isWebViewAvailable()) {
   2841             return;
   2842         }
   2843 
   2844         WebView.startSafeBrowsing(getActivity().getApplicationContext(), null);
   2845     }
   2846 
   2847     public void testStartSafeBrowsingInvokesCallback() throws Exception {
   2848         if (!NullWebViewUtils.isWebViewAvailable()) {
   2849             return;
   2850         }
   2851 
   2852         final CountDownLatch resultLatch = new CountDownLatch(1);
   2853         WebView.startSafeBrowsing(getActivity().getApplicationContext(),
   2854                 new ValueCallback<Boolean>() {
   2855             @Override
   2856             public void onReceiveValue(Boolean value) {
   2857                 assertTrue(Looper.getMainLooper().isCurrentThread());
   2858                 resultLatch.countDown();
   2859                 return;
   2860             }
   2861         });
   2862         assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
   2863     }
   2864 
   2865     private void savePrintedPage(final PrintDocumentAdapter adapter,
   2866             final ParcelFileDescriptor descriptor, final PageRange[] pageRanges,
   2867             final FutureTask<Boolean> result) {
   2868         adapter.onWrite(pageRanges, descriptor,
   2869                 new CancellationSignal(),
   2870                 new WriteResultCallback() {
   2871                     @Override
   2872                     public void onWriteFinished(PageRange[] pages) {
   2873                         try {
   2874                             descriptor.close();
   2875                             result.run();
   2876                         } catch (IOException ex) {
   2877                             fail("Failed file operation: " + ex.toString());
   2878                         }
   2879                     }
   2880                 });
   2881     }
   2882 
   2883     private void printDocumentStart(final PrintDocumentAdapter adapter) {
   2884         mOnUiThread.runOnUiThread(new Runnable() {
   2885             @Override
   2886             public void run() {
   2887                 adapter.onStart();
   2888             }
   2889         });
   2890     }
   2891 
   2892     private void printDocumentLayout(final PrintDocumentAdapter adapter,
   2893             final PrintAttributes oldAttributes, final PrintAttributes newAttributes,
   2894             final LayoutResultCallback layoutResultCallback) {
   2895         mOnUiThread.runOnUiThread(new Runnable() {
   2896             @Override
   2897             public void run() {
   2898                 adapter.onLayout(oldAttributes, newAttributes, new CancellationSignal(),
   2899                         layoutResultCallback, null);
   2900             }
   2901         });
   2902     }
   2903 
   2904     private static class HrefCheckHandler extends Handler {
   2905         private boolean mHadRecieved;
   2906 
   2907         private String mResultUrl;
   2908 
   2909         public HrefCheckHandler(Looper looper) {
   2910             super(looper);
   2911         }
   2912 
   2913         public boolean hasCalledHandleMessage() {
   2914             return mHadRecieved;
   2915         }
   2916 
   2917         public String getResultUrl() {
   2918             return mResultUrl;
   2919         }
   2920 
   2921         public void reset(){
   2922             mResultUrl = null;
   2923             mHadRecieved = false;
   2924         }
   2925 
   2926         @Override
   2927         public void handleMessage(Message msg) {
   2928             mResultUrl = msg.getData().getString("url");
   2929             mHadRecieved = true;
   2930         }
   2931     }
   2932 
   2933     private void moveFocusDown() throws Throwable {
   2934         // send down key and wait for idle
   2935         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
   2936         // waiting for idle isn't always sufficient for the key to be fully processed
   2937         Thread.sleep(500);
   2938     }
   2939 
   2940     private void pollingCheckWebBackForwardList(final String currUrl, final int currIndex,
   2941             final int size) {
   2942         new PollingCheck() {
   2943             @Override
   2944             protected boolean check() {
   2945                 WebBackForwardList list = mWebView.copyBackForwardList();
   2946                 return checkWebBackForwardList(list, currUrl, currIndex, size);
   2947             }
   2948         }.run();
   2949     }
   2950 
   2951     private boolean checkWebBackForwardList(WebBackForwardList list, String currUrl,
   2952             int currIndex, int size) {
   2953         return (list != null)
   2954                 && (list.getSize() == size)
   2955                 && (list.getCurrentIndex() == currIndex)
   2956                 && list.getItemAtIndex(currIndex).getUrl().equals(currUrl);
   2957     }
   2958 
   2959     private void assertGoBackOrForwardBySteps(boolean expected, int steps) {
   2960         // skip if steps equals to 0
   2961         if (steps == 0)
   2962             return;
   2963 
   2964         int start = steps > 0 ? 1 : steps;
   2965         int end = steps > 0 ? steps : -1;
   2966 
   2967         // check all the steps in the history
   2968         for (int i = start; i <= end; i++) {
   2969             assertEquals(expected, mWebView.canGoBackOrForward(i));
   2970 
   2971             // shortcut methods for one step
   2972             if (i == 1) {
   2973                 assertEquals(expected, mWebView.canGoForward());
   2974             } else if (i == -1) {
   2975                 assertEquals(expected, mWebView.canGoBack());
   2976             }
   2977         }
   2978     }
   2979 
   2980     private boolean isPictureFilledWithColor(Picture picture, int color) {
   2981         if (picture.getWidth() == 0 || picture.getHeight() == 0)
   2982             return false;
   2983 
   2984         Bitmap bitmap = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(),
   2985                 Config.ARGB_8888);
   2986         picture.draw(new Canvas(bitmap));
   2987 
   2988         for (int i = 0; i < bitmap.getWidth(); i ++) {
   2989             for (int j = 0; j < bitmap.getHeight(); j ++) {
   2990                 if (color != bitmap.getPixel(i, j)) {
   2991                     return false;
   2992                 }
   2993             }
   2994         }
   2995         return true;
   2996     }
   2997 
   2998     /**
   2999      * Waits at least MIN_SCROLL_WAIT_MS for scrolling to start. Once started,
   3000      * scrolling is checked every SCROLL_WAIT_INTERVAL_MS for changes. Once
   3001      * changes have stopped, the function exits. If no scrolling has happened
   3002      * then the function exits after MIN_SCROLL_WAIT milliseconds.
   3003      * @param previousScrollY The Y scroll position prior to waiting.
   3004      */
   3005     private void waitForScrollingComplete(int previousScrollY)
   3006             throws InterruptedException {
   3007         int scrollY = previousScrollY;
   3008         // wait at least MIN_SCROLL_WAIT for something to happen.
   3009         long noChangeMinWait = SystemClock.uptimeMillis() + MIN_SCROLL_WAIT_MS;
   3010         boolean scrollChanging = false;
   3011         boolean scrollChanged = false;
   3012         boolean minWaitExpired = false;
   3013         while (scrollChanging || (!scrollChanged && !minWaitExpired)) {
   3014             Thread.sleep(SCROLL_WAIT_INTERVAL_MS);
   3015             int oldScrollY = scrollY;
   3016             scrollY = mOnUiThread.getScrollY();
   3017             scrollChanging = (scrollY != oldScrollY);
   3018             scrollChanged = (scrollY != previousScrollY);
   3019             minWaitExpired = (SystemClock.uptimeMillis() > noChangeMinWait);
   3020         }
   3021     }
   3022 
   3023     private void pollingCheckForCanZoomIn() {
   3024         new PollingCheck(TEST_TIMEOUT) {
   3025             @Override
   3026             protected boolean check() {
   3027                 return mOnUiThread.canZoomIn();
   3028             }
   3029         }.run();
   3030     }
   3031 
   3032     final class ScaleChangedWebViewClient extends WaitForLoadedClient {
   3033         private boolean mOnScaleChangedCalled = false;
   3034         public ScaleChangedWebViewClient() {
   3035             super(mOnUiThread);
   3036         }
   3037 
   3038         @Override
   3039         public void onScaleChanged(WebView view, float oldScale, float newScale) {
   3040             super.onScaleChanged(view, oldScale, newScale);
   3041             synchronized (this) {
   3042                 mOnScaleChangedCalled = true;
   3043             }
   3044         }
   3045 
   3046         public void waitForScaleChanged() {
   3047             new PollingCheck(TEST_TIMEOUT) {
   3048                  @Override
   3049                  protected boolean check() {
   3050                      return onScaleChangedCalled();
   3051                  }
   3052             }.run();
   3053             synchronized (this) {
   3054                 mOnScaleChangedCalled = false;
   3055             }
   3056         }
   3057 
   3058         public synchronized boolean onScaleChangedCalled() {
   3059             return mOnScaleChangedCalled;
   3060         }
   3061     }
   3062 
   3063     public void testGetSafeBrowsingPrivacyPolicyUrl() throws Exception {
   3064         if (!NullWebViewUtils.isWebViewAvailable()) {
   3065             return;
   3066         }
   3067 
   3068         assertNotNull(WebView.getSafeBrowsingPrivacyPolicyUrl());
   3069         try {
   3070             new URL(WebView.getSafeBrowsingPrivacyPolicyUrl().toString());
   3071         } catch (MalformedURLException e) {
   3072             Assert.fail("The privacy policy URL should be a well-formed URL");
   3073         }
   3074     }
   3075 
   3076     public void testWebViewClassLoaderReturnsNonNull() {
   3077         if (!NullWebViewUtils.isWebViewAvailable()) {
   3078             return;
   3079         }
   3080 
   3081         assertNotNull(WebView.getWebViewClassLoader());
   3082     }
   3083 }
   3084