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.res.AssetManager;
     22 import android.cts.util.EvaluateJsResultPollingCheck;
     23 import android.cts.util.PollingCheck;
     24 import android.graphics.Bitmap;
     25 import android.graphics.Bitmap.Config;
     26 import android.graphics.BitmapFactory;
     27 import android.graphics.Canvas;
     28 import android.graphics.Color;
     29 import android.graphics.Picture;
     30 import android.graphics.Rect;
     31 import android.net.Uri;
     32 import android.net.http.SslCertificate;
     33 import android.net.http.SslError;
     34 import android.os.Bundle;
     35 import android.os.CancellationSignal;
     36 import android.os.Handler;
     37 import android.os.Looper;
     38 import android.os.Message;
     39 import android.os.ParcelFileDescriptor;
     40 import android.os.StrictMode;
     41 import android.os.StrictMode.ThreadPolicy;
     42 import android.os.SystemClock;
     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.webkit.ConsoleMessage;
     58 import android.webkit.CookieSyncManager;
     59 import android.webkit.DownloadListener;
     60 import android.webkit.JavascriptInterface;
     61 import android.webkit.SslErrorHandler;
     62 import android.webkit.ValueCallback;
     63 import android.webkit.WebBackForwardList;
     64 import android.webkit.WebChromeClient;
     65 import android.webkit.WebIconDatabase;
     66 import android.webkit.WebSettings;
     67 import android.webkit.WebView;
     68 import android.webkit.WebView.HitTestResult;
     69 import android.webkit.WebView.PictureListener;
     70 import android.webkit.WebViewClient;
     71 import android.webkit.WebViewDatabase;
     72 import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
     73 import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
     74 import android.widget.LinearLayout;
     75 
     76 import junit.framework.Assert;
     77 
     78 import java.io.File;
     79 import java.io.FileInputStream;
     80 import java.io.IOException;
     81 import java.util.Collections;
     82 import java.util.Date;
     83 import java.util.concurrent.atomic.AtomicReference;
     84 import java.util.concurrent.Callable;
     85 import java.util.concurrent.FutureTask;
     86 import java.util.concurrent.TimeUnit;
     87 import java.util.HashMap;
     88 
     89 import org.apache.http.Header;
     90 import org.apache.http.HttpRequest;
     91 
     92 public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
     93     private static final String LOGTAG = "WebViewTest";
     94     private static final int INITIAL_PROGRESS = 100;
     95     private static long TEST_TIMEOUT = 20000L;
     96     private static final String X_REQUESTED_WITH = "X-Requested-With";
     97     private static final String PRINTER_TEST_FILE = "print.pdf";
     98     private static final String PDF_PREAMBLE = "%PDF-1";
     99 
    100     /**
    101      * This is the minimum number of milliseconds to wait for scrolling to
    102      * start. If no scrolling has started before this timeout then it is
    103      * assumed that no scrolling will happen.
    104      */
    105     private static final long MIN_SCROLL_WAIT_MS = 1000;
    106     /**
    107      * Once scrolling has started, this is the interval that scrolling
    108      * is checked to see if there is a change. If no scrolling change
    109      * has happened in the given time then it is assumed that scrolling
    110      * has stopped.
    111      */
    112     private static final long SCROLL_WAIT_INTERVAL_MS = 200;
    113 
    114     private WebView mWebView;
    115     private CtsTestServer mWebServer;
    116     private WebViewOnUiThread mOnUiThread;
    117     private WebIconDatabase mIconDb;
    118 
    119     public WebViewTest() {
    120         super("com.android.cts.stub", WebViewStubActivity.class);
    121     }
    122 
    123     @Override
    124     protected void setUp() throws Exception {
    125         super.setUp();
    126         final WebViewStubActivity activity = getActivity();
    127         new PollingCheck() {
    128             @Override
    129                 protected boolean check() {
    130                 return activity.hasWindowFocus();
    131             }
    132         }.run();
    133         mWebView = activity.getWebView();
    134         File f = activity.getFileStreamPath("snapshot");
    135         if (f.exists()) {
    136             f.delete();
    137         }
    138         mOnUiThread = new WebViewOnUiThread(this, mWebView);
    139     }
    140 
    141     @Override
    142     protected void tearDown() throws Exception {
    143         mOnUiThread.cleanUp();
    144         if (mWebServer != null) {
    145             mWebServer.shutdown();
    146         }
    147         if (mIconDb != null) {
    148             mIconDb.removeAllIcons();
    149             mIconDb.close();
    150             mIconDb = null;
    151         }
    152         super.tearDown();
    153     }
    154 
    155     private void startWebServer(boolean secure) throws Exception {
    156         assertNull(mWebServer);
    157         mWebServer = new CtsTestServer(getActivity(), secure);
    158     }
    159 
    160     private void stopWebServer() throws Exception {
    161         assertNotNull(mWebServer);
    162         ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
    163         ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy)
    164                 .permitNetwork()
    165                 .build();
    166         StrictMode.setThreadPolicy(tmpPolicy);
    167         mWebServer.shutdown();
    168         mWebServer = null;
    169         StrictMode.setThreadPolicy(oldPolicy);
    170     }
    171 
    172     @UiThreadTest
    173     public void testConstructor() {
    174         new WebView(getActivity());
    175         new WebView(getActivity(), null);
    176         new WebView(getActivity(), null, 0);
    177     }
    178 
    179     @UiThreadTest
    180     public void testCreatingWebViewCreatesCookieSyncManager() throws Exception {
    181         new WebView(getActivity());
    182         assertNotNull(CookieSyncManager.getInstance());
    183     }
    184 
    185     @UiThreadTest
    186     public void testFindAddress() {
    187         /*
    188          * Info about USPS
    189          * http://en.wikipedia.org/wiki/Postal_address#United_States
    190          * http://www.usps.com/
    191          */
    192         // full address
    193         assertEquals("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826",
    194                 WebView.findAddress("455 LARKSPUR DRIVE CALIFORNIA SPRINGS CALIFORNIA 92826"));
    195         // not an address
    196         assertNull(WebView.findAddress("This is not an address: no town, no state, no zip."));
    197     }
    198 
    199     @SuppressWarnings("deprecation")
    200     @UiThreadTest
    201     public void testGetZoomControls() {
    202          WebSettings settings = mWebView.getSettings();
    203          assertTrue(settings.supportZoom());
    204          View zoomControls = mWebView.getZoomControls();
    205          assertNotNull(zoomControls);
    206 
    207          // disable zoom support
    208          settings.setSupportZoom(false);
    209          assertFalse(settings.supportZoom());
    210          assertNull(mWebView.getZoomControls());
    211     }
    212 
    213     @UiThreadTest
    214     public void testInvokeZoomPicker() throws Exception {
    215         WebSettings settings = mWebView.getSettings();
    216         assertTrue(settings.supportZoom());
    217         startWebServer(false);
    218         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    219         mOnUiThread.loadUrlAndWaitForCompletion(url);
    220         mWebView.invokeZoomPicker();
    221     }
    222 
    223     public void testZoom() throws Throwable {
    224         // Pinch zoom is not supported in wrap_content layouts.
    225         mOnUiThread.setLayoutHeightToMatchParent();
    226 
    227         final ScaleChangedWebViewClient webViewClient = new ScaleChangedWebViewClient();
    228         mOnUiThread.setWebViewClient(webViewClient);
    229 
    230         mWebServer = new CtsTestServer(getActivity());
    231         mOnUiThread.loadUrlAndWaitForCompletion(
    232                 mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
    233 
    234         WebSettings settings = mOnUiThread.getSettings();
    235         settings.setSupportZoom(false);
    236         assertFalse(settings.supportZoom());
    237         float currScale = mOnUiThread.getScale();
    238         float previousScale = currScale;
    239 
    240         // can zoom in or out although zoom support is disabled in web settings
    241         assertTrue(mOnUiThread.zoomIn());
    242         webViewClient.waitForScaleChanged();
    243 
    244         currScale = mOnUiThread.getScale();
    245         assertTrue(currScale > previousScale);
    246 
    247         assertTrue(mOnUiThread.zoomOut());
    248         previousScale = currScale;
    249         webViewClient.waitForScaleChanged();
    250 
    251         currScale = mOnUiThread.getScale();
    252         assertTrue(currScale < previousScale);
    253 
    254         // enable zoom support
    255         settings.setSupportZoom(true);
    256         assertTrue(settings.supportZoom());
    257         previousScale = mOnUiThread.getScale();
    258 
    259         assertTrue(mOnUiThread.zoomIn());
    260         webViewClient.waitForScaleChanged();
    261 
    262         currScale = mOnUiThread.getScale();
    263         assertTrue(currScale > previousScale);
    264 
    265         // zoom in until it reaches maximum scale
    266         while (mOnUiThread.zoomIn()) {
    267             previousScale = currScale;
    268             webViewClient.waitForScaleChanged();
    269             currScale = mOnUiThread.getScale();
    270             assertTrue(currScale > previousScale);
    271         }
    272 
    273         previousScale = currScale;
    274         // can not zoom in further
    275         assertFalse(mOnUiThread.zoomIn());
    276         // We sleep to assert to the best of our ability
    277         // that a scale change does *not* happen.
    278         Thread.sleep(500);
    279         currScale = mOnUiThread.getScale();
    280         assertEquals(currScale, previousScale);
    281 
    282         // zoom out
    283         assertTrue(mOnUiThread.zoomOut());
    284         previousScale = currScale;
    285         webViewClient.waitForScaleChanged();
    286         currScale = mOnUiThread.getScale();
    287         assertTrue(currScale < previousScale);
    288 
    289         // zoom out until it reaches minimum scale
    290         while (mOnUiThread.zoomOut()) {
    291             previousScale = currScale;
    292             webViewClient.waitForScaleChanged();
    293             currScale = mOnUiThread.getScale();
    294             assertTrue(currScale < previousScale);
    295         }
    296 
    297         previousScale = currScale;
    298         assertFalse(mOnUiThread.zoomOut());
    299 
    300         // We sleep to assert to the best of our ability
    301         // that a scale change does *not* happen.
    302         Thread.sleep(500);
    303         currScale = mOnUiThread.getScale();
    304         assertEquals(currScale, previousScale);
    305     }
    306 
    307     @UiThreadTest
    308     public void testSetScrollBarStyle() {
    309         mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
    310         assertFalse(mWebView.overlayHorizontalScrollbar());
    311         assertFalse(mWebView.overlayVerticalScrollbar());
    312 
    313         mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
    314         assertTrue(mWebView.overlayHorizontalScrollbar());
    315         assertTrue(mWebView.overlayVerticalScrollbar());
    316 
    317         mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
    318         assertFalse(mWebView.overlayHorizontalScrollbar());
    319         assertFalse(mWebView.overlayVerticalScrollbar());
    320 
    321         mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
    322         assertTrue(mWebView.overlayHorizontalScrollbar());
    323         assertTrue(mWebView.overlayVerticalScrollbar());
    324     }
    325 
    326     @UiThreadTest
    327     public void testScrollBarOverlay() throws Throwable {
    328         mWebView.setHorizontalScrollbarOverlay(true);
    329         mWebView.setVerticalScrollbarOverlay(false);
    330         assertTrue(mWebView.overlayHorizontalScrollbar());
    331         assertFalse(mWebView.overlayVerticalScrollbar());
    332 
    333         mWebView.setHorizontalScrollbarOverlay(false);
    334         mWebView.setVerticalScrollbarOverlay(true);
    335         assertFalse(mWebView.overlayHorizontalScrollbar());
    336         assertTrue(mWebView.overlayVerticalScrollbar());
    337     }
    338 
    339     @UiThreadTest
    340     public void testLoadUrl() throws Exception {
    341         assertNull(mWebView.getUrl());
    342         assertNull(mWebView.getOriginalUrl());
    343         assertEquals(INITIAL_PROGRESS, mWebView.getProgress());
    344 
    345         startWebServer(false);
    346         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    347         mOnUiThread.loadUrlAndWaitForCompletion(url);
    348         assertEquals(100, mWebView.getProgress());
    349         assertEquals(url, mWebView.getUrl());
    350         assertEquals(url, mWebView.getOriginalUrl());
    351         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle());
    352 
    353         // verify that the request also includes X-Requested-With header
    354         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
    355         Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
    356         assertEquals(1, matchingHeaders.length);
    357 
    358         Header header = matchingHeaders[0];
    359         assertEquals(mWebView.getContext().getApplicationInfo().packageName, header.getValue());
    360     }
    361 
    362     @UiThreadTest
    363     public void testLoadUrlDoesNotStripParamsWhenLoadingContentUrls() throws Exception {
    364         Uri.Builder uriBuilder = new Uri.Builder().scheme(
    365                 ContentResolver.SCHEME_CONTENT).authority(MockContentProvider.AUTHORITY);
    366         uriBuilder.appendPath("foo.html").appendQueryParameter("param","bar");
    367         String url = uriBuilder.build().toString();
    368         mOnUiThread.loadUrlAndWaitForCompletion(url);
    369         // verify the parameter is not stripped.
    370         Uri uri = Uri.parse(mWebView.getTitle());
    371         assertEquals("bar", uri.getQueryParameter("param"));
    372     }
    373 
    374     @UiThreadTest
    375     public void testAppInjectedXRequestedWithHeaderIsNotOverwritten() throws Exception {
    376         startWebServer(false);
    377         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    378         HashMap<String, String> map = new HashMap<String, String>();
    379         final String requester = "foo";
    380         map.put(X_REQUESTED_WITH, requester);
    381         mOnUiThread.loadUrlAndWaitForCompletion(url, map);
    382 
    383         // verify that the request also includes X-Requested-With header
    384         // but is not overwritten by the webview
    385         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
    386         Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
    387         assertEquals(1, matchingHeaders.length);
    388 
    389         Header header = matchingHeaders[0];
    390         assertEquals(requester, header.getValue());
    391     }
    392 
    393     @UiThreadTest
    394     public void testAppCanInjectHeadersViaImmutableMap() throws Exception {
    395         startWebServer(false);
    396         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    397         HashMap<String, String> map = new HashMap<String, String>();
    398         final String requester = "foo";
    399         map.put(X_REQUESTED_WITH, requester);
    400         mOnUiThread.loadUrlAndWaitForCompletion(url, Collections.unmodifiableMap(map));
    401 
    402         // verify that the request also includes X-Requested-With header
    403         // but is not overwritten by the webview
    404         HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
    405         Header[] matchingHeaders = request.getHeaders(X_REQUESTED_WITH);
    406         assertEquals(1, matchingHeaders.length);
    407 
    408         Header header = matchingHeaders[0];
    409         assertEquals(requester, header.getValue());
    410     }
    411 
    412     @SuppressWarnings("deprecation")
    413     @UiThreadTest
    414     public void testGetVisibleTitleHeight() throws Exception {
    415         startWebServer(false);
    416         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    417         mOnUiThread.loadUrlAndWaitForCompletion(url);
    418         assertEquals(0, mWebView.getVisibleTitleHeight());
    419     }
    420 
    421     @UiThreadTest
    422     public void testGetOriginalUrl() throws Throwable {
    423         startWebServer(false);
    424         final String finalUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    425         final String redirectUrl =
    426                 mWebServer.getRedirectingAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    427 
    428         assertNull(mWebView.getUrl());
    429         assertNull(mWebView.getOriginalUrl());
    430 
    431         // By default, WebView sends an intent to ask the system to
    432         // handle loading a new URL. We set a WebViewClient as
    433         // WebViewClient.shouldOverrideUrlLoading() returns false, so
    434         // the WebView will load the new URL.
    435         mOnUiThread.setWebViewClient(new WaitForLoadedClient(mOnUiThread));
    436         mOnUiThread.loadUrlAndWaitForCompletion(redirectUrl);
    437 
    438         assertEquals(finalUrl, mWebView.getUrl());
    439         assertEquals(redirectUrl, mWebView.getOriginalUrl());
    440     }
    441 
    442     public void testStopLoading() throws Exception {
    443         assertEquals(INITIAL_PROGRESS, mOnUiThread.getProgress());
    444 
    445         startWebServer(false);
    446         String url = mWebServer.getDelayedAssetUrl(TestHtmlConstants.STOP_LOADING_URL);
    447 
    448         class JsInterface {
    449             private boolean mPageLoaded;
    450 
    451             @JavascriptInterface
    452             public synchronized void pageLoaded() {
    453                 mPageLoaded = true;
    454                 notify();
    455             }
    456             public synchronized boolean getPageLoaded() {
    457                 return mPageLoaded;
    458             }
    459         }
    460 
    461         JsInterface jsInterface = new JsInterface();
    462 
    463         mOnUiThread.getSettings().setJavaScriptEnabled(true);
    464         mOnUiThread.addJavascriptInterface(jsInterface, "javabridge");
    465         mOnUiThread.loadUrl(url);
    466         mOnUiThread.stopLoading();
    467 
    468         // We wait to see that the onload callback in the HTML is not fired.
    469         synchronized (jsInterface) {
    470             jsInterface.wait(3000);
    471         }
    472 
    473         assertFalse(jsInterface.getPageLoaded());
    474     }
    475 
    476     @UiThreadTest
    477     public void testGoBackAndForward() throws Exception {
    478         assertGoBackOrForwardBySteps(false, -1);
    479         assertGoBackOrForwardBySteps(false, 1);
    480 
    481         startWebServer(false);
    482         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
    483         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
    484         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
    485 
    486         mOnUiThread.loadUrlAndWaitForCompletion(url1);
    487         pollingCheckWebBackForwardList(url1, 0, 1);
    488         assertGoBackOrForwardBySteps(false, -1);
    489         assertGoBackOrForwardBySteps(false, 1);
    490 
    491         mOnUiThread.loadUrlAndWaitForCompletion(url2);
    492         pollingCheckWebBackForwardList(url2, 1, 2);
    493         assertGoBackOrForwardBySteps(true, -1);
    494         assertGoBackOrForwardBySteps(false, 1);
    495 
    496         mOnUiThread.loadUrlAndWaitForCompletion(url3);
    497         pollingCheckWebBackForwardList(url3, 2, 3);
    498         assertGoBackOrForwardBySteps(true, -2);
    499         assertGoBackOrForwardBySteps(false, 1);
    500 
    501         mWebView.goBack();
    502         pollingCheckWebBackForwardList(url2, 1, 3);
    503         assertGoBackOrForwardBySteps(true, -1);
    504         assertGoBackOrForwardBySteps(true, 1);
    505 
    506         mWebView.goForward();
    507         pollingCheckWebBackForwardList(url3, 2, 3);
    508         assertGoBackOrForwardBySteps(true, -2);
    509         assertGoBackOrForwardBySteps(false, 1);
    510 
    511         mWebView.goBackOrForward(-2);
    512         pollingCheckWebBackForwardList(url1, 0, 3);
    513         assertGoBackOrForwardBySteps(false, -1);
    514         assertGoBackOrForwardBySteps(true, 2);
    515 
    516         mWebView.goBackOrForward(2);
    517         pollingCheckWebBackForwardList(url3, 2, 3);
    518         assertGoBackOrForwardBySteps(true, -2);
    519         assertGoBackOrForwardBySteps(false, 1);
    520     }
    521 
    522     @UiThreadTest
    523     public void testAddJavascriptInterface() throws Exception {
    524         WebSettings settings = mWebView.getSettings();
    525         settings.setJavaScriptEnabled(true);
    526         settings.setJavaScriptCanOpenWindowsAutomatically(true);
    527 
    528         final class DummyJavaScriptInterface {
    529             private boolean mWasProvideResultCalled;
    530             private String mResult;
    531 
    532             private synchronized String waitForResult() {
    533                 while (!mWasProvideResultCalled) {
    534                     try {
    535                         wait(TEST_TIMEOUT);
    536                     } catch (InterruptedException e) {
    537                         continue;
    538                     }
    539                     if (!mWasProvideResultCalled) {
    540                         Assert.fail("Unexpected timeout");
    541                     }
    542                 }
    543                 return mResult;
    544             }
    545 
    546             public synchronized boolean wasProvideResultCalled() {
    547                 return mWasProvideResultCalled;
    548             }
    549 
    550             @JavascriptInterface
    551             public synchronized void provideResult(String result) {
    552                 mWasProvideResultCalled = true;
    553                 mResult = result;
    554                 notify();
    555             }
    556         }
    557 
    558         final DummyJavaScriptInterface obj = new DummyJavaScriptInterface();
    559         mWebView.addJavascriptInterface(obj, "dummy");
    560         assertFalse(obj.wasProvideResultCalled());
    561 
    562         startWebServer(false);
    563         String url = mWebServer.getAssetUrl(TestHtmlConstants.ADD_JAVA_SCRIPT_INTERFACE_URL);
    564         mOnUiThread.loadUrlAndWaitForCompletion(url);
    565         assertEquals("Original title", obj.waitForResult());
    566     }
    567 
    568     @UiThreadTest
    569     public void testAddJavascriptInterfaceNullObject() throws Exception {
    570         WebSettings settings = mWebView.getSettings();
    571         settings.setJavaScriptEnabled(true);
    572         String setTitleToPropertyTypeHtml = "<html><head></head>" +
    573                 "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>";
    574 
    575         // Test that the property is initially undefined.
    576         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    577                 "text/html", null);
    578         assertEquals("undefined", mWebView.getTitle());
    579 
    580         // Test that adding a null object has no effect.
    581         mWebView.addJavascriptInterface(null, "injectedObject");
    582         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    583                 "text/html", null);
    584         assertEquals("undefined", mWebView.getTitle());
    585 
    586         // Test that adding an object gives an object type.
    587         final Object obj = new Object();
    588         mWebView.addJavascriptInterface(obj, "injectedObject");
    589         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    590                 "text/html", null);
    591         assertEquals("object", mWebView.getTitle());
    592 
    593         // Test that trying to replace with a null object has no effect.
    594         mWebView.addJavascriptInterface(null, "injectedObject");
    595         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    596                 "text/html", null);
    597         assertEquals("object", mWebView.getTitle());
    598     }
    599 
    600     @UiThreadTest
    601     public void testRemoveJavascriptInterface() throws Exception {
    602         WebSettings settings = mWebView.getSettings();
    603         settings.setJavaScriptEnabled(true);
    604         String setTitleToPropertyTypeHtml = "<html><head></head>" +
    605                 "<body onload=\"document.title = typeof window.injectedObject;\"></body></html>";
    606 
    607         // Test that adding an object gives an object type.
    608         mWebView.addJavascriptInterface(new Object(), "injectedObject");
    609         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    610                 "text/html", null);
    611         assertEquals("object", mWebView.getTitle());
    612 
    613         // Test that reloading the page after removing the object leaves the property undefined.
    614         mWebView.removeJavascriptInterface("injectedObject");
    615         mOnUiThread.loadDataAndWaitForCompletion(setTitleToPropertyTypeHtml,
    616                 "text/html", null);
    617         assertEquals("undefined", mWebView.getTitle());
    618     }
    619 
    620     public void testUseRemovedJavascriptInterface() throws Throwable {
    621         class RemovedObject {
    622             @Override
    623             @JavascriptInterface
    624             public String toString() {
    625                 return "removedObject";
    626             }
    627 
    628             @JavascriptInterface
    629             public void remove() throws Throwable {
    630                 mOnUiThread.removeJavascriptInterface("removedObject");
    631                 System.gc();
    632             }
    633         }
    634         class ResultObject {
    635             private String mResult;
    636             private boolean mIsResultAvailable;
    637 
    638             @JavascriptInterface
    639             public synchronized void setResult(String result) {
    640                 mResult = result;
    641                 mIsResultAvailable = true;
    642                 notify();
    643             }
    644             public synchronized String getResult() {
    645                 while (!mIsResultAvailable) {
    646                     try {
    647                         wait();
    648                     } catch (InterruptedException e) {
    649                     }
    650                 }
    651                 return mResult;
    652             }
    653         }
    654         final ResultObject resultObject = new ResultObject();
    655 
    656         // Test that an object is still usable if removed while the page is in use, even if we have
    657         // no external references to it.
    658         mOnUiThread.getSettings().setJavaScriptEnabled(true);
    659         mOnUiThread.addJavascriptInterface(new RemovedObject(), "removedObject");
    660         mOnUiThread.addJavascriptInterface(resultObject, "resultObject");
    661         mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" +
    662                 "<body onload=\"window.removedObject.remove();" +
    663                 "resultObject.setResult(removedObject.toString());\"></body></html>",
    664                 "text/html", null);
    665         assertEquals("removedObject", resultObject.getResult());
    666     }
    667 
    668     private final class TestPictureListener implements PictureListener {
    669         public int callCount;
    670 
    671         @Override
    672         public void onNewPicture(WebView view, Picture picture) {
    673             // Need to inform the listener tracking new picture
    674             // for the "page loaded" knowledge since it has been replaced.
    675             mOnUiThread.onNewPicture();
    676             this.callCount += 1;
    677         }
    678     }
    679 
    680     private Picture waitForPictureToHaveColor(int color,
    681             final TestPictureListener listener) throws Throwable {
    682         final int MAX_ON_NEW_PICTURE_ITERATIONS = 5;
    683         final AtomicReference<Picture> pictureRef = new AtomicReference<Picture>();
    684         for (int i = 0; i < MAX_ON_NEW_PICTURE_ITERATIONS; i++) {
    685             final int oldCallCount = listener.callCount;
    686             runTestOnUiThread(new Runnable() {
    687                 @Override
    688                 public void run() {
    689                     pictureRef.set(mWebView.capturePicture());
    690                 }
    691             });
    692             if (isPictureFilledWithColor(pictureRef.get(), color))
    693                 break;
    694             new PollingCheck(TEST_TIMEOUT) {
    695                 @Override
    696                 protected boolean check() {
    697                     return listener.callCount > oldCallCount;
    698                 }
    699             }.run();
    700         }
    701         return pictureRef.get();
    702     }
    703 
    704     public void testCapturePicture() throws Exception, Throwable {
    705         final TestPictureListener listener = new TestPictureListener();
    706 
    707         startWebServer(false);
    708         final String url = mWebServer.getAssetUrl(TestHtmlConstants.BLANK_PAGE_URL);
    709         mOnUiThread.setPictureListener(listener);
    710         // Showing the blank page will fill the picture with the background color.
    711         mOnUiThread.loadUrlAndWaitForCompletion(url);
    712         // The default background color is white.
    713         Picture oldPicture = waitForPictureToHaveColor(Color.WHITE, listener);
    714 
    715         runTestOnUiThread(new Runnable() {
    716             @Override
    717             public void run() {
    718                 mWebView.setBackgroundColor(Color.CYAN);
    719             }
    720         });
    721         mOnUiThread.reloadAndWaitForCompletion();
    722         waitForPictureToHaveColor(Color.CYAN, listener);
    723 
    724         // The content of the previously captured picture will not be updated automatically.
    725         assertTrue(isPictureFilledWithColor(oldPicture, Color.WHITE));
    726     }
    727 
    728     public void testSetPictureListener() throws Exception, Throwable {
    729         final class MyPictureListener implements PictureListener {
    730             public int callCount;
    731             public WebView webView;
    732             public Picture picture;
    733 
    734             @Override
    735             public void onNewPicture(WebView view, Picture picture) {
    736                 // Need to inform the listener tracking new picture
    737                 // for the "page loaded" knowledge since it has been replaced.
    738                 mOnUiThread.onNewPicture();
    739                 this.callCount += 1;
    740                 this.webView = view;
    741                 this.picture = picture;
    742             }
    743         }
    744 
    745         final MyPictureListener listener = new MyPictureListener();
    746         startWebServer(false);
    747         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    748         mOnUiThread.setPictureListener(listener);
    749         mOnUiThread.loadUrlAndWaitForCompletion(url);
    750         new PollingCheck(TEST_TIMEOUT) {
    751             @Override
    752             protected boolean check() {
    753                 return listener.callCount > 0;
    754             }
    755         }.run();
    756         assertEquals(mWebView, listener.webView);
    757         assertNull(listener.picture);
    758 
    759         final int oldCallCount = listener.callCount;
    760         final String newUrl = mWebServer.getAssetUrl(TestHtmlConstants.SMALL_IMG_URL);
    761         mOnUiThread.loadUrlAndWaitForCompletion(newUrl);
    762         new PollingCheck(TEST_TIMEOUT) {
    763             @Override
    764             protected boolean check() {
    765                 return listener.callCount > oldCallCount;
    766             }
    767         }.run();
    768     }
    769 
    770     @UiThreadTest
    771     public void testAccessHttpAuthUsernamePassword() {
    772         try {
    773             WebViewDatabase.getInstance(getActivity()).clearHttpAuthUsernamePassword();
    774 
    775             String host = "http://localhost:8080";
    776             String realm = "testrealm";
    777             String userName = "user";
    778             String password = "password";
    779 
    780             String[] result = mWebView.getHttpAuthUsernamePassword(host, realm);
    781             assertNull(result);
    782 
    783             mWebView.setHttpAuthUsernamePassword(host, realm, userName, password);
    784             result = mWebView.getHttpAuthUsernamePassword(host, realm);
    785             assertNotNull(result);
    786             assertEquals(userName, result[0]);
    787             assertEquals(password, result[1]);
    788 
    789             String newPassword = "newpassword";
    790             mWebView.setHttpAuthUsernamePassword(host, realm, userName, newPassword);
    791             result = mWebView.getHttpAuthUsernamePassword(host, realm);
    792             assertNotNull(result);
    793             assertEquals(userName, result[0]);
    794             assertEquals(newPassword, result[1]);
    795 
    796             String newUserName = "newuser";
    797             mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
    798             result = mWebView.getHttpAuthUsernamePassword(host, realm);
    799             assertNotNull(result);
    800             assertEquals(newUserName, result[0]);
    801             assertEquals(newPassword, result[1]);
    802 
    803             // the user is set to null, can not change any thing in the future
    804             mWebView.setHttpAuthUsernamePassword(host, realm, null, password);
    805             result = mWebView.getHttpAuthUsernamePassword(host, realm);
    806             assertNotNull(result);
    807             assertNull(result[0]);
    808             assertEquals(password, result[1]);
    809 
    810             mWebView.setHttpAuthUsernamePassword(host, realm, userName, null);
    811             result = mWebView.getHttpAuthUsernamePassword(host, realm);
    812             assertNotNull(result);
    813             assertEquals(userName, result[0]);
    814             assertEquals(null, result[1]);
    815 
    816             mWebView.setHttpAuthUsernamePassword(host, realm, null, null);
    817             result = mWebView.getHttpAuthUsernamePassword(host, realm);
    818             assertNotNull(result);
    819             assertNull(result[0]);
    820             assertNull(result[1]);
    821 
    822             mWebView.setHttpAuthUsernamePassword(host, realm, newUserName, newPassword);
    823             result = mWebView.getHttpAuthUsernamePassword(host, realm);
    824             assertNotNull(result);
    825             assertEquals(newUserName, result[0]);
    826             assertEquals(newPassword, result[1]);
    827         } finally {
    828             WebViewDatabase.getInstance(getActivity()).clearHttpAuthUsernamePassword();
    829         }
    830     }
    831 
    832     public void testLoadData() throws Throwable {
    833         final String HTML_CONTENT =
    834                 "<html><head><title>Hello,World!</title></head><body></body>" +
    835                 "</html>";
    836         mOnUiThread.loadDataAndWaitForCompletion(HTML_CONTENT,
    837                 "text/html", null);
    838         assertEquals("Hello,World!", mOnUiThread.getTitle());
    839 
    840         startWebServer(false);
    841         final ChromeClient webChromeClient = new ChromeClient(mOnUiThread);
    842         final String crossOriginUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    843         runTestOnUiThread(new Runnable() {
    844             @Override
    845             public void run() {
    846                 mWebView.getSettings().setJavaScriptEnabled(true);
    847                 mOnUiThread.setWebChromeClient(webChromeClient);
    848                 mOnUiThread.loadDataAndWaitForCompletion(
    849                         "<html><head></head><body onload=\"" +
    850                         "document.title = " +
    851                         "document.getElementById('frame').contentWindow.location.href;" +
    852                         "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
    853                         "text/html", null);
    854             }
    855         });
    856         assertEquals(ConsoleMessage.MessageLevel.ERROR, webChromeClient.getMessageLevel(10000));
    857     }
    858 
    859     @UiThreadTest
    860     public void testLoadDataWithBaseUrl() throws Throwable {
    861         assertNull(mWebView.getUrl());
    862         String imgUrl = TestHtmlConstants.SMALL_IMG_URL; // relative
    863         // Snippet of HTML that will prevent favicon requests to the test server.
    864         final String HTML_HEADER = "<html><head><link rel=\"shortcut icon\" href=\"#\" /></head>";
    865 
    866         // Trying to resolve a relative URL against a data URL without a base URL
    867         // will fail and we won't make a request to the test web server.
    868         // By using the test web server as the base URL we expect to see a request
    869         // for the relative URL in the test server.
    870         startWebServer(false);
    871         String baseUrl = mWebServer.getAssetUrl("foo.html");
    872         String historyUrl = "http://www.example.com/";
    873         mWebServer.resetRequestState();
    874         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
    875                 HTML_HEADER + "<body><img src=\"" + imgUrl + "\"/></body></html>",
    876                 "text/html", "UTF-8", historyUrl);
    877         // Verify that the resource request makes it to the server.
    878         assertTrue(mWebServer.wasResourceRequested(imgUrl));
    879         assertEquals(historyUrl, mWebView.getUrl());
    880         assertEquals(historyUrl, mWebView.getOriginalUrl());
    881 
    882         // Check that reported URL is "about:blank" when supplied history URL
    883         // is null.
    884         imgUrl = TestHtmlConstants.LARGE_IMG_URL;
    885         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
    886                 HTML_HEADER + "<body><img src=\"" + imgUrl + "\"/></body></html>",
    887                 "text/html", "UTF-8", null);
    888         assertTrue(mWebServer.wasResourceRequested(imgUrl));
    889         assertEquals("about:blank", mWebView.getUrl());
    890         assertEquals("about:blank", mWebView.getOriginalUrl());
    891 
    892         // Test that JavaScript can access content from the same origin as the base URL.
    893         mWebView.getSettings().setJavaScriptEnabled(true);
    894         final String crossOriginUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
    895         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(baseUrl,
    896                 HTML_HEADER + "<body onload=\"" +
    897                 "document.title = document.getElementById('frame').contentWindow.location.href;" +
    898                 "\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
    899                 "text/html", "UTF-8", null);
    900         assertEquals(crossOriginUrl, mWebView.getTitle());
    901 
    902         // Check that when the base URL uses the 'data' scheme, a 'data' scheme URL is used and the
    903         // history URL is ignored.
    904         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
    905                 HTML_HEADER + "<body>bar</body></html>", "text/html", "UTF-8",
    906                 historyUrl);
    907         assertTrue("URL: " + mWebView.getUrl(), mWebView.getUrl().indexOf("data:text/html") == 0);
    908         assertTrue("URL: " + mWebView.getUrl(), mWebView.getUrl().indexOf("bar") > 0);
    909 
    910         // Check that when a non-data: base URL is used, we treat the String to load as
    911         // a raw string and just dump it into the WebView, i.e. not decoding any URL entities.
    912         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("http://www.foo.com",
    913                 HTML_HEADER + "<title>Hello World%21</title><body>bar</body></html>",
    914                 "text/html", "UTF-8", null);
    915         assertEquals("Hello World%21", mWebView.getTitle());
    916 
    917         // Check that when a data: base URL is used, we treat the String to load as a data: URL
    918         // and run load steps such as decoding URL entities (i.e., contrary to the test case
    919         // above.)
    920         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
    921                 HTML_HEADER + "<title>Hello World%21</title></html>", "text/html", "UTF-8", null);
    922         assertEquals("Hello World!", mWebView.getTitle());
    923 
    924         // Check the method is null input safe.
    925         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(null, null, null, null, null);
    926         assertEquals("about:blank", mWebView.getUrl());
    927         assertEquals("about:blank", mWebView.getOriginalUrl());
    928     }
    929 
    930     private static class WaitForFindResultsListener extends FutureTask<Integer>
    931             implements WebView.FindListener {
    932         public WaitForFindResultsListener() {
    933             super(new Runnable() {
    934                 @Override
    935                 public void run() { }
    936             }, null);
    937         }
    938 
    939         @Override
    940         public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
    941                 boolean isDoneCounting) {
    942             if (isDoneCounting) {
    943                 set(numberOfMatches);
    944             }
    945         }
    946     }
    947 
    948     public void testFindAll()  throws Throwable {
    949         // Make the page scrollable, so we can detect the scrolling to make sure the
    950         // content fully loaded.
    951         mOnUiThread.setInitialScale(100);
    952         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
    953         int dimension = Math.max(metrics.widthPixels, metrics.heightPixels);
    954         // create a paragraph high enough to take up the entire screen
    955         String p = "<p style=\"height:" + dimension + "px;\">" +
    956                 "Find all instances of find on the page and highlight them.</p>";
    957 
    958         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
    959                 + "</body></html>", "text/html", null);
    960 
    961         WaitForFindResultsListener l = new WaitForFindResultsListener();
    962         int previousScrollY = mOnUiThread.getScrollY();
    963         mOnUiThread.pageDown(true);
    964         // Wait for content fully loaded.
    965         waitForScrollingComplete(previousScrollY);
    966         mOnUiThread.setFindListener(l);
    967         mOnUiThread.findAll("find");
    968 
    969         assertEquals(2, l.get().intValue());
    970     }
    971 
    972     public void testFindNext() throws Throwable {
    973         // Reset the scaling so that finding the next "all" text will require scrolling.
    974         mOnUiThread.setInitialScale(100);
    975 
    976         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
    977         int dimension = Math.max(metrics.widthPixels, metrics.heightPixels);
    978         // create a paragraph high enough to take up the entire screen
    979         String p = "<p style=\"height:" + dimension + "px;\">" +
    980                 "Find all instances of a word on the page and highlight them.</p>";
    981 
    982         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p + p + "</body></html>", "text/html", null);
    983 
    984         // highlight all the strings found
    985         mOnUiThread.findAll("all");
    986         getInstrumentation().waitForIdleSync();
    987 
    988         int previousScrollY = mOnUiThread.getScrollY();
    989 
    990         // Focus "all" in the second page and assert that the view scrolls.
    991         mOnUiThread.findNext(true);
    992         waitForScrollingComplete(previousScrollY);
    993         assertTrue(mOnUiThread.getScrollY() > previousScrollY);
    994         previousScrollY = mOnUiThread.getScrollY();
    995 
    996         // Focus "all" in the first page and assert that the view scrolls.
    997         mOnUiThread.findNext(true);
    998         waitForScrollingComplete(previousScrollY);
    999         assertTrue(mOnUiThread.getScrollY() < previousScrollY);
   1000         previousScrollY = mOnUiThread.getScrollY();
   1001 
   1002         // Focus "all" in the second page and assert that the view scrolls.
   1003         mOnUiThread.findNext(false);
   1004         waitForScrollingComplete(previousScrollY);
   1005         assertTrue(mOnUiThread.getScrollY() > previousScrollY);
   1006         previousScrollY = mOnUiThread.getScrollY();
   1007 
   1008         // Focus "all" in the first page and assert that the view scrolls.
   1009         mOnUiThread.findNext(false);
   1010         waitForScrollingComplete(previousScrollY);
   1011         assertTrue(mOnUiThread.getScrollY() < previousScrollY);
   1012         previousScrollY = mOnUiThread.getScrollY();
   1013 
   1014         // clear the result
   1015         mOnUiThread.clearMatches();
   1016         getInstrumentation().waitForIdleSync();
   1017 
   1018         // can not scroll any more
   1019         mOnUiThread.findNext(false);
   1020         waitForScrollingComplete(previousScrollY);
   1021         assertTrue(mOnUiThread.getScrollY() == previousScrollY);
   1022 
   1023         mOnUiThread.findNext(true);
   1024         waitForScrollingComplete(previousScrollY);
   1025         assertTrue(mOnUiThread.getScrollY() == previousScrollY);
   1026     }
   1027 
   1028     public void testDocumentHasImages() throws Exception, Throwable {
   1029         final class DocumentHasImageCheckHandler extends Handler {
   1030             private boolean mReceived;
   1031             private int mMsgArg1;
   1032             public DocumentHasImageCheckHandler(Looper looper) {
   1033                 super(looper);
   1034             }
   1035             @Override
   1036             public void handleMessage(Message msg) {
   1037                 synchronized(this) {
   1038                     mReceived = true;
   1039                     mMsgArg1 = msg.arg1;
   1040                 }
   1041             }
   1042             public synchronized boolean hasCalledHandleMessage() {
   1043                 return mReceived;
   1044             }
   1045             public synchronized int getMsgArg1() {
   1046                 return mMsgArg1;
   1047             }
   1048         }
   1049 
   1050         startWebServer(false);
   1051         final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.SMALL_IMG_URL);
   1052 
   1053         // Create a handler on the UI thread.
   1054         final DocumentHasImageCheckHandler handler =
   1055             new DocumentHasImageCheckHandler(mWebView.getHandler().getLooper());
   1056 
   1057         runTestOnUiThread(new Runnable() {
   1058             @Override
   1059             public void run() {
   1060                 mOnUiThread.loadDataAndWaitForCompletion("<html><body><img src=\""
   1061                         + imgUrl + "\"/></body></html>", "text/html", null);
   1062                 Message response = new Message();
   1063                 response.setTarget(handler);
   1064                 assertFalse(handler.hasCalledHandleMessage());
   1065                 mWebView.documentHasImages(response);
   1066             }
   1067         });
   1068         new PollingCheck() {
   1069             @Override
   1070             protected boolean check() {
   1071                 return handler.hasCalledHandleMessage();
   1072             }
   1073         }.run();
   1074         assertEquals(1, handler.getMsgArg1());
   1075     }
   1076 
   1077     public void testPageScroll() throws Throwable {
   1078         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
   1079         int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
   1080         String p = "<p style=\"height:" + dimension + "px;\">" +
   1081                 "Scroll by half the size of the page.</p>";
   1082         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1083                 + p + "</body></html>", "text/html", null);
   1084 
   1085         // Wait for UI thread to settle and receive page dimentions from renderer
   1086         // such that we can invoke page down.
   1087         new PollingCheck() {
   1088             @Override
   1089             protected boolean check() {
   1090                  return mOnUiThread.pageDown(false);
   1091             }
   1092         }.run();
   1093 
   1094         do {
   1095             getInstrumentation().waitForIdleSync();
   1096         } while (mOnUiThread.pageDown(false));
   1097 
   1098         getInstrumentation().waitForIdleSync();
   1099         int bottomScrollY = mOnUiThread.getScrollY();
   1100 
   1101         assertTrue(mOnUiThread.pageUp(false));
   1102 
   1103         do {
   1104             getInstrumentation().waitForIdleSync();
   1105         } while (mOnUiThread.pageUp(false));
   1106 
   1107         getInstrumentation().waitForIdleSync();
   1108         int topScrollY = mOnUiThread.getScrollY();
   1109 
   1110         // jump to the bottom
   1111         assertTrue(mOnUiThread.pageDown(true));
   1112         getInstrumentation().waitForIdleSync();
   1113         assertEquals(bottomScrollY, mOnUiThread.getScrollY());
   1114 
   1115         // jump to the top
   1116         assertTrue(mOnUiThread.pageUp(true));
   1117         getInstrumentation().waitForIdleSync();
   1118         assertEquals(topScrollY, mOnUiThread.getScrollY());
   1119     }
   1120 
   1121     public void testGetContentHeight() throws Throwable {
   1122         mOnUiThread.loadDataAndWaitForCompletion(
   1123                 "<html><body></body></html>", "text/html", null);
   1124         new PollingCheck() {
   1125             @Override
   1126             protected boolean check() {
   1127                 return mOnUiThread.getScale() != 0 && mOnUiThread.getContentHeight() != 0
   1128                     && mOnUiThread.getHeight() != 0;
   1129             }
   1130         }.run();
   1131         assertEquals(mOnUiThread.getHeight(),
   1132                 mOnUiThread.getContentHeight() * mOnUiThread.getScale(), 2f);
   1133 
   1134         final int pageHeight = 600;
   1135         // set the margin to 0
   1136         final String p = "<p style=\"height:" + pageHeight
   1137                 + "px;margin:0px auto;\">Get the height of HTML content.</p>";
   1138         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1139                 + "</body></html>", "text/html", null);
   1140         new PollingCheck() {
   1141             @Override
   1142             protected boolean check() {
   1143                 return mOnUiThread.getContentHeight() > pageHeight;
   1144             }
   1145         }.run();
   1146 
   1147         final int extraSpace = mOnUiThread.getContentHeight() - pageHeight;
   1148         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1149                 + p + "</body></html>", "text/html", null);
   1150         new PollingCheck() {
   1151             @Override
   1152             protected boolean check() {
   1153                 return pageHeight + pageHeight + extraSpace == mOnUiThread.getContentHeight();
   1154             }
   1155         }.run();
   1156     }
   1157 
   1158     @UiThreadTest
   1159     public void testPlatformNotifications() {
   1160         WebView.enablePlatformNotifications();
   1161         WebView.disablePlatformNotifications();
   1162     }
   1163 
   1164     @UiThreadTest
   1165     public void testAccessPluginList() {
   1166         assertNotNull(WebView.getPluginList());
   1167 
   1168         // can not find a way to install plugins
   1169         mWebView.refreshPlugins(false);
   1170     }
   1171 
   1172     @UiThreadTest
   1173     public void testDestroy() {
   1174         // Create a new WebView, since we cannot call destroy() on a view in the hierarchy
   1175         WebView localWebView = new WebView(getActivity());
   1176         localWebView.destroy();
   1177     }
   1178 
   1179     public void testFlingScroll() throws Throwable {
   1180         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
   1181         final int dimension = 10 * Math.max(metrics.widthPixels, metrics.heightPixels);
   1182         String p = "<p style=\"height:" + dimension + "px;" +
   1183                 "width:" + dimension + "px\">Test fling scroll.</p>";
   1184         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1185                 + "</body></html>", "text/html", null);
   1186         new PollingCheck() {
   1187             @Override
   1188             protected boolean check() {
   1189                 return mOnUiThread.getContentHeight() >= dimension;
   1190             }
   1191         }.run();
   1192         getInstrumentation().waitForIdleSync();
   1193 
   1194         final int previousScrollX = mOnUiThread.getScrollX();
   1195         final int previousScrollY = mOnUiThread.getScrollY();
   1196 
   1197         mOnUiThread.flingScroll(100, 100);
   1198 
   1199         new PollingCheck() {
   1200             @Override
   1201             protected boolean check() {
   1202                 return mOnUiThread.getScrollX() > previousScrollX &&
   1203                         mOnUiThread.getScrollY() > previousScrollY;
   1204             }
   1205         }.run();
   1206     }
   1207 
   1208     public void testRequestFocusNodeHref() throws Throwable {
   1209         startWebServer(false);
   1210         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
   1211         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   1212         final String links = "<DL><p><DT><A HREF=\"" + url1
   1213                 + "\">HTML_URL1</A><DT><A HREF=\"" + url2
   1214                 + "\">HTML_URL2</A></DL><p>";
   1215         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + links + "</body></html>", "text/html", null);
   1216         getInstrumentation().waitForIdleSync();
   1217 
   1218         final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper());
   1219         final Message hrefMsg = new Message();
   1220         hrefMsg.setTarget(handler);
   1221 
   1222         // focus on first link
   1223         handler.reset();
   1224         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
   1225         mOnUiThread.requestFocusNodeHref(hrefMsg);
   1226         new PollingCheck() {
   1227             @Override
   1228             protected boolean check() {
   1229                 boolean done = false;
   1230                 if (handler.hasCalledHandleMessage()) {
   1231                     if (handler.mResultUrl != null) {
   1232                         done = true;
   1233                     } else {
   1234                         handler.reset();
   1235                         Message newMsg = new Message();
   1236                         newMsg.setTarget(handler);
   1237                         mOnUiThread.requestFocusNodeHref(newMsg);
   1238                     }
   1239                 }
   1240                 return done;
   1241             }
   1242         }.run();
   1243         assertEquals(url1, handler.getResultUrl());
   1244 
   1245         // focus on second link
   1246         handler.reset();
   1247         final Message hrefMsg2 = new Message();
   1248         hrefMsg2.setTarget(handler);
   1249         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
   1250         mOnUiThread.requestFocusNodeHref(hrefMsg2);
   1251         new PollingCheck() {
   1252             @Override
   1253             protected boolean check() {
   1254                 boolean done = false;
   1255                 final String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   1256                 if (handler.hasCalledHandleMessage()) {
   1257                     if (handler.mResultUrl != null &&
   1258                             handler.mResultUrl.equals(url2)) {
   1259                         done = true;
   1260                     } else {
   1261                         handler.reset();
   1262                         Message newMsg = new Message();
   1263                         newMsg.setTarget(handler);
   1264                         mOnUiThread.requestFocusNodeHref(newMsg);
   1265                     }
   1266                 }
   1267                 return done;
   1268             }
   1269         }.run();
   1270         assertEquals(url2, handler.getResultUrl());
   1271 
   1272         mOnUiThread.requestFocusNodeHref(null);
   1273     }
   1274 
   1275     public void testRequestImageRef() throws Exception, Throwable {
   1276         final class ImageLoaded {
   1277             public boolean mImageLoaded;
   1278 
   1279             @JavascriptInterface
   1280             public void loaded() {
   1281                 mImageLoaded = true;
   1282             }
   1283         }
   1284         final ImageLoaded imageLoaded = new ImageLoaded();
   1285         runTestOnUiThread(new Runnable() {
   1286             public void run() {
   1287                 mOnUiThread.getSettings().setJavaScriptEnabled(true);
   1288             }
   1289         });
   1290         mOnUiThread.addJavascriptInterface(imageLoaded, "imageLoaded");
   1291         AssetManager assets = getActivity().getAssets();
   1292         Bitmap bitmap = BitmapFactory.decodeStream(assets.open(TestHtmlConstants.LARGE_IMG_URL));
   1293         int imgWidth = bitmap.getWidth();
   1294         int imgHeight = bitmap.getHeight();
   1295 
   1296         startWebServer(false);
   1297         final String imgUrl = mWebServer.getAssetUrl(TestHtmlConstants.LARGE_IMG_URL);
   1298         mOnUiThread.loadDataAndWaitForCompletion(
   1299                 "<html><head><title>Title</title><style type=\"text/css\">"
   1300                 + "#imgElement { -webkit-transform: translate3d(0,0,1); }"
   1301                 + "#imgElement.finish { -webkit-transform: translate3d(0,0,0);"
   1302                 + " -webkit-transition-duration: 1ms; }</style>"
   1303                 + "<script type=\"text/javascript\">function imgLoad() {"
   1304                 + "imgElement = document.getElementById('imgElement');"
   1305                 + "imgElement.addEventListener('webkitTransitionEnd',"
   1306                 + "function(e) { imageLoaded.loaded(); });"
   1307                 + "imgElement.className = 'finish';}</script>"
   1308                 + "</head><body><img id=\"imgElement\" src=\"" + imgUrl
   1309                 + "\" width=\"" + imgWidth + "\" height=\"" + imgHeight
   1310                 + "\" onLoad=\"imgLoad()\"/></body></html>", "text/html", null);
   1311         new PollingCheck() {
   1312             @Override
   1313             protected boolean check() {
   1314                 return imageLoaded.mImageLoaded;
   1315             }
   1316         }.run();
   1317         getInstrumentation().waitForIdleSync();
   1318 
   1319         final HrefCheckHandler handler = new HrefCheckHandler(mWebView.getHandler().getLooper());
   1320         final Message msg = new Message();
   1321         msg.setTarget(handler);
   1322 
   1323         // touch the image
   1324         handler.reset();
   1325         int[] location = mOnUiThread.getLocationOnScreen();
   1326 
   1327         long time = SystemClock.uptimeMillis();
   1328         getInstrumentation().sendPointerSync(
   1329                 MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN,
   1330                         location[0] + imgWidth / 2,
   1331                         location[1] + imgHeight / 2, 0));
   1332         getInstrumentation().waitForIdleSync();
   1333         mOnUiThread.requestImageRef(msg);
   1334         new PollingCheck() {
   1335             @Override
   1336             protected boolean check() {
   1337                 boolean done = false;
   1338                 if (handler.hasCalledHandleMessage()) {
   1339                     if (handler.mResultUrl != null) {
   1340                         done = true;
   1341                     } else {
   1342                         handler.reset();
   1343                         Message newMsg = new Message();
   1344                         newMsg.setTarget(handler);
   1345                         mOnUiThread.requestImageRef(newMsg);
   1346                     }
   1347                 }
   1348                 return done;
   1349             }
   1350         }.run();
   1351         assertEquals(imgUrl, handler.mResultUrl);
   1352     }
   1353 
   1354     @UiThreadTest
   1355     public void testDebugDump() {
   1356         mWebView.debugDump();
   1357     }
   1358 
   1359     public void testGetHitTestResult() throws Throwable {
   1360         final String anchor = "<p><a href=\"" + TestHtmlConstants.EXT_WEB_URL1
   1361                 + "\">normal anchor</a></p>";
   1362         final String blankAnchor = "<p><a href=\"\">blank anchor</a></p>";
   1363         final String form = "<p><form><input type=\"text\" name=\"Test\"><br>"
   1364                 + "<input type=\"submit\" value=\"Submit\"></form></p>";
   1365         String phoneNo = "3106984000";
   1366         final String tel = "<p><a href=\"tel:" + phoneNo + "\">Phone</a></p>";
   1367         String email = "test (at) gmail.com";
   1368         final String mailto = "<p><a href=\"mailto:" + email + "\">Email</a></p>";
   1369         String location = "shanghai";
   1370         final String geo = "<p><a href=\"geo:0,0?q=" + location + "\">Location</a></p>";
   1371 
   1372         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("fake://home",
   1373                 "<html><body>" + anchor + blankAnchor + form + tel + mailto +
   1374                 geo + "</body></html>", "text/html", "UTF-8", null);
   1375         getInstrumentation().waitForIdleSync();
   1376 
   1377         // anchor
   1378         moveFocusDown();
   1379         HitTestResult hitTestResult = mOnUiThread.getHitTestResult();
   1380         assertEquals(HitTestResult.SRC_ANCHOR_TYPE, hitTestResult.getType());
   1381         assertEquals(TestHtmlConstants.EXT_WEB_URL1, hitTestResult.getExtra());
   1382 
   1383         // blank anchor
   1384         moveFocusDown();
   1385         hitTestResult = mOnUiThread.getHitTestResult();
   1386         assertEquals(HitTestResult.SRC_ANCHOR_TYPE, hitTestResult.getType());
   1387         assertEquals("fake://home", hitTestResult.getExtra());
   1388 
   1389         // text field
   1390         moveFocusDown();
   1391         hitTestResult = mOnUiThread.getHitTestResult();
   1392         assertEquals(HitTestResult.EDIT_TEXT_TYPE, hitTestResult.getType());
   1393         assertNull(hitTestResult.getExtra());
   1394 
   1395         // submit button
   1396         moveFocusDown();
   1397         hitTestResult = mOnUiThread.getHitTestResult();
   1398         assertEquals(HitTestResult.UNKNOWN_TYPE, hitTestResult.getType());
   1399         assertNull(hitTestResult.getExtra());
   1400 
   1401         // phone number
   1402         moveFocusDown();
   1403         hitTestResult = mOnUiThread.getHitTestResult();
   1404         assertEquals(HitTestResult.PHONE_TYPE, hitTestResult.getType());
   1405         assertEquals(phoneNo, hitTestResult.getExtra());
   1406 
   1407         // email
   1408         moveFocusDown();
   1409         hitTestResult = mOnUiThread.getHitTestResult();
   1410         assertEquals(HitTestResult.EMAIL_TYPE, hitTestResult.getType());
   1411         assertEquals(email, hitTestResult.getExtra());
   1412 
   1413         // geo address
   1414         moveFocusDown();
   1415         hitTestResult = mOnUiThread.getHitTestResult();
   1416         assertEquals(HitTestResult.GEO_TYPE, hitTestResult.getType());
   1417         assertEquals(location, hitTestResult.getExtra());
   1418     }
   1419 
   1420     public void testSetInitialScale() throws Throwable {
   1421         final String p = "<p style=\"height:1000px;width:1000px\">Test setInitialScale.</p>";
   1422         final float defaultScale =
   1423             getInstrumentation().getTargetContext().getResources().getDisplayMetrics().density;
   1424 
   1425         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1426                 + "</body></html>", "text/html", null);
   1427 
   1428         new PollingCheck(TEST_TIMEOUT) {
   1429             @Override
   1430             protected boolean check() {
   1431                 return Math.abs(defaultScale - mOnUiThread.getScale()) < .01f;
   1432             }
   1433         }.run();
   1434 
   1435         mOnUiThread.setInitialScale(0);
   1436         // modify content to fool WebKit into re-loading
   1437         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1438                 + "2" + "</body></html>", "text/html", null);
   1439 
   1440         new PollingCheck(TEST_TIMEOUT) {
   1441             @Override
   1442             protected boolean check() {
   1443                 return Math.abs(defaultScale - mOnUiThread.getScale()) < .01f;
   1444             }
   1445         }.run();
   1446 
   1447         mOnUiThread.setInitialScale(50);
   1448         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1449                 + "3" + "</body></html>", "text/html", null);
   1450 
   1451         new PollingCheck(TEST_TIMEOUT) {
   1452             @Override
   1453             protected boolean check() {
   1454                 return Math.abs(0.5 - mOnUiThread.getScale()) < .01f;
   1455             }
   1456         }.run();
   1457 
   1458         mOnUiThread.setInitialScale(0);
   1459         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1460                 + "4" + "</body></html>", "text/html", null);
   1461 
   1462         new PollingCheck(TEST_TIMEOUT) {
   1463             @Override
   1464             protected boolean check() {
   1465                 return Math.abs(defaultScale - mOnUiThread.getScale()) < .01f;
   1466             }
   1467         }.run();
   1468     }
   1469 
   1470     @UiThreadTest
   1471     public void testGetFavicon() throws Exception {
   1472         startWebServer(false);
   1473         String url = mWebServer.getAssetUrl(TestHtmlConstants.TEST_FAVICON_URL);
   1474         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1475         mWebView.getFavicon();
   1476         // ToBeFixed: Favicon is not loaded automatically.
   1477         // assertNotNull(mWebView.getFavicon());
   1478     }
   1479 
   1480     @UiThreadTest
   1481     public void testClearHistory() throws Exception {
   1482         startWebServer(false);
   1483         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
   1484         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   1485         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
   1486 
   1487         mOnUiThread.loadUrlAndWaitForCompletion(url1);
   1488         pollingCheckWebBackForwardList(url1, 0, 1);
   1489 
   1490         mOnUiThread.loadUrlAndWaitForCompletion(url2);
   1491         pollingCheckWebBackForwardList(url2, 1, 2);
   1492 
   1493         mOnUiThread.loadUrlAndWaitForCompletion(url3);
   1494         pollingCheckWebBackForwardList(url3, 2, 3);
   1495 
   1496         mWebView.clearHistory();
   1497 
   1498         // only current URL is left after clearing
   1499         pollingCheckWebBackForwardList(url3, 0, 1);
   1500     }
   1501 
   1502     @UiThreadTest
   1503     public void testSaveAndRestoreState() throws Throwable {
   1504         // nothing to save
   1505         assertNull(mWebView.saveState(new Bundle()));
   1506 
   1507         startWebServer(false);
   1508         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
   1509         String url2 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   1510         String url3 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL3);
   1511 
   1512         // make a history list
   1513         mOnUiThread.loadUrlAndWaitForCompletion(url1);
   1514         pollingCheckWebBackForwardList(url1, 0, 1);
   1515         mOnUiThread.loadUrlAndWaitForCompletion(url2);
   1516         pollingCheckWebBackForwardList(url2, 1, 2);
   1517         mOnUiThread.loadUrlAndWaitForCompletion(url3);
   1518         pollingCheckWebBackForwardList(url3, 2, 3);
   1519 
   1520         // save the list
   1521         Bundle bundle = new Bundle();
   1522         WebBackForwardList saveList = mWebView.saveState(bundle);
   1523         assertNotNull(saveList);
   1524         assertEquals(3, saveList.getSize());
   1525         assertEquals(2, saveList.getCurrentIndex());
   1526         assertEquals(url1, saveList.getItemAtIndex(0).getUrl());
   1527         assertEquals(url2, saveList.getItemAtIndex(1).getUrl());
   1528         assertEquals(url3, saveList.getItemAtIndex(2).getUrl());
   1529 
   1530         // change the content to a new "blank" web view without history
   1531         final WebView newWebView = new WebView(getActivity());
   1532 
   1533         WebBackForwardList copyListBeforeRestore = newWebView.copyBackForwardList();
   1534         assertNotNull(copyListBeforeRestore);
   1535         assertEquals(0, copyListBeforeRestore.getSize());
   1536 
   1537         // restore the list
   1538         final WebBackForwardList restoreList = newWebView.restoreState(bundle);
   1539         assertNotNull(restoreList);
   1540         assertEquals(3, restoreList.getSize());
   1541         assertEquals(2, saveList.getCurrentIndex());
   1542         /* ToBeFixed: The WebHistoryItems do not get inflated. Uncomment remaining tests when fixed.
   1543         // wait for the list items to get inflated
   1544         new PollingCheck(TEST_TIMEOUT) {
   1545             @Override
   1546             protected boolean check() {
   1547                 return restoreList.getItemAtIndex(0).getUrl() != null &&
   1548                        restoreList.getItemAtIndex(1).getUrl() != null &&
   1549                        restoreList.getItemAtIndex(2).getUrl() != null;
   1550             }
   1551         }.run();
   1552         assertEquals(url1, restoreList.getItemAtIndex(0).getUrl());
   1553         assertEquals(url2, restoreList.getItemAtIndex(1).getUrl());
   1554         assertEquals(url3, restoreList.getItemAtIndex(2).getUrl());
   1555 
   1556         WebBackForwardList copyListAfterRestore = newWebView.copyBackForwardList();
   1557         assertNotNull(copyListAfterRestore);
   1558         assertEquals(3, copyListAfterRestore.getSize());
   1559         assertEquals(2, copyListAfterRestore.getCurrentIndex());
   1560         assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl());
   1561         assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl());
   1562         assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl());
   1563         */
   1564     }
   1565 
   1566     public void testSetWebViewClient() throws Throwable {
   1567         final ScaleChangedWebViewClient webViewClient = new ScaleChangedWebViewClient();
   1568         mOnUiThread.setWebViewClient(webViewClient);
   1569         startWebServer(false);
   1570 
   1571         assertFalse(webViewClient.onScaleChangedCalled());
   1572         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1573         mOnUiThread.loadUrlAndWaitForCompletion(url1);
   1574 
   1575         mOnUiThread.zoomIn();
   1576         webViewClient.waitForScaleChanged();
   1577     }
   1578 
   1579     @UiThreadTest
   1580     public void testInsecureSiteClearsCertificate() throws Throwable {
   1581         final class MockWebViewClient extends WaitForLoadedClient {
   1582             public MockWebViewClient() {
   1583                 super(mOnUiThread);
   1584             }
   1585             @Override
   1586             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   1587                 handler.proceed();
   1588             }
   1589         }
   1590 
   1591         startWebServer(true);
   1592         mOnUiThread.setWebViewClient(new MockWebViewClient());
   1593         mOnUiThread.loadUrlAndWaitForCompletion(
   1594                 mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
   1595         SslCertificate cert = mWebView.getCertificate();
   1596         assertNotNull(cert);
   1597         assertEquals("Android", cert.getIssuedTo().getUName());
   1598 
   1599         stopWebServer();
   1600 
   1601         startWebServer(false);
   1602         mOnUiThread.loadUrlAndWaitForCompletion(
   1603                 mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
   1604         assertNull(mWebView.getCertificate());
   1605     }
   1606 
   1607     @UiThreadTest
   1608     public void testSecureSiteSetsCertificate() throws Throwable {
   1609         final class MockWebViewClient extends WaitForLoadedClient {
   1610             public MockWebViewClient() {
   1611                 super(mOnUiThread);
   1612             }
   1613             @Override
   1614             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   1615                 handler.proceed();
   1616             }
   1617         }
   1618 
   1619         startWebServer(false);
   1620         mOnUiThread.loadUrlAndWaitForCompletion(
   1621                 mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
   1622         assertNull(mWebView.getCertificate());
   1623 
   1624         stopWebServer();
   1625 
   1626         startWebServer(true);
   1627         mOnUiThread.setWebViewClient(new MockWebViewClient());
   1628         mOnUiThread.loadUrlAndWaitForCompletion(
   1629                 mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
   1630         SslCertificate cert = mWebView.getCertificate();
   1631         assertNotNull(cert);
   1632         assertEquals("Android", cert.getIssuedTo().getUName());
   1633     }
   1634 
   1635     @UiThreadTest
   1636     public void testClearSslPreferences() throws Throwable {
   1637         // Load the first page. We expect a call to
   1638         // WebViewClient.onReceivedSslError().
   1639         final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
   1640         startWebServer(true);
   1641         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1642         mOnUiThread.setWebViewClient(webViewClient);
   1643         mOnUiThread.clearSslPreferences();
   1644         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1645         assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
   1646 
   1647         // Load the page again. We expect another call to
   1648         // WebViewClient.onReceivedSslError() since we cleared sslpreferences.
   1649         mOnUiThread.clearSslPreferences();
   1650         webViewClient.resetWasOnReceivedSslErrorCalled();
   1651         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1652         assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
   1653         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
   1654 
   1655         // Load the page once again, without clearing the sslpreferences.
   1656         // Make sure we do not get the callback.
   1657         webViewClient.resetWasOnReceivedSslErrorCalled();
   1658         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1659         assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
   1660         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
   1661     }
   1662 
   1663     public void testOnReceivedSslError() throws Throwable {
   1664         final class MockWebViewClient extends WaitForLoadedClient {
   1665             private String mErrorUrl;
   1666             private WebView mWebView;
   1667 
   1668             public MockWebViewClient() {
   1669                 super(mOnUiThread);
   1670             }
   1671             @Override
   1672             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   1673                 mWebView = view;
   1674                 mErrorUrl = error.getUrl();
   1675                 handler.proceed();
   1676             }
   1677             public String errorUrl() {
   1678                 return mErrorUrl;
   1679             }
   1680             public WebView webView() {
   1681                 return mWebView;
   1682             }
   1683         }
   1684 
   1685         startWebServer(true);
   1686         final String errorUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1687         final MockWebViewClient webViewClient = new MockWebViewClient();
   1688         mOnUiThread.setWebViewClient(webViewClient);
   1689         mOnUiThread.clearSslPreferences();
   1690         mOnUiThread.loadUrlAndWaitForCompletion(errorUrl);
   1691 
   1692         assertEquals(mWebView, webViewClient.webView());
   1693         assertEquals(errorUrl, webViewClient.errorUrl());
   1694     }
   1695 
   1696     public void testOnReceivedSslErrorProceed() throws Throwable {
   1697         final class MockWebViewClient extends WaitForLoadedClient {
   1698             public MockWebViewClient() {
   1699                 super(mOnUiThread);
   1700             }
   1701             @Override
   1702             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   1703                 handler.proceed();
   1704             }
   1705         }
   1706 
   1707         startWebServer(true);
   1708         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1709         mOnUiThread.setWebViewClient(new MockWebViewClient());
   1710         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1711         assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
   1712     }
   1713 
   1714     public void testOnReceivedSslErrorCancel() throws Throwable {
   1715         final class MockWebViewClient extends WaitForLoadedClient {
   1716             public MockWebViewClient() {
   1717                 super(mOnUiThread);
   1718             }
   1719             @Override
   1720             public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   1721                 handler.cancel();
   1722             }
   1723         }
   1724 
   1725         startWebServer(true);
   1726         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1727         mOnUiThread.setWebViewClient(new MockWebViewClient());
   1728         mOnUiThread.clearSslPreferences();
   1729         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1730         assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
   1731     }
   1732 
   1733     public void testSslErrorProceedResponseReusedForSameHost() throws Throwable {
   1734         // Load the first page. We expect a call to
   1735         // WebViewClient.onReceivedSslError().
   1736         final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
   1737         startWebServer(true);
   1738         final String firstUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
   1739         mOnUiThread.setWebViewClient(webViewClient);
   1740         mOnUiThread.clearSslPreferences();
   1741         mOnUiThread.loadUrlAndWaitForCompletion(firstUrl);
   1742         assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
   1743 
   1744         // Load the second page. We don't expect a call to
   1745         // WebViewClient.onReceivedSslError(), but the page should load.
   1746         webViewClient.resetWasOnReceivedSslErrorCalled();
   1747         final String sameHostUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
   1748         mOnUiThread.loadUrlAndWaitForCompletion(sameHostUrl);
   1749         assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
   1750         assertEquals("Second page", mOnUiThread.getTitle());
   1751     }
   1752 
   1753     public void testSslErrorProceedResponseNotReusedForDifferentHost() throws Throwable {
   1754         // Load the first page. We expect a call to
   1755         // WebViewClient.onReceivedSslError().
   1756         final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
   1757         startWebServer(true);
   1758         final String firstUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
   1759         mOnUiThread.setWebViewClient(webViewClient);
   1760         mOnUiThread.clearSslPreferences();
   1761         mOnUiThread.loadUrlAndWaitForCompletion(firstUrl);
   1762         assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
   1763 
   1764         // Load the second page. We expect another call to
   1765         // WebViewClient.onReceivedSslError().
   1766         webViewClient.resetWasOnReceivedSslErrorCalled();
   1767         // The test server uses the host "localhost". "127.0.0.1" works as an
   1768         // alias, but will be considered unique by the WebView.
   1769         final String differentHostUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2).replace(
   1770                 "localhost", "127.0.0.1");
   1771         mOnUiThread.loadUrlAndWaitForCompletion(differentHostUrl);
   1772         assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
   1773         assertEquals("Second page", mOnUiThread.getTitle());
   1774     }
   1775 
   1776     public void testRequestChildRectangleOnScreen() throws Throwable {
   1777         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
   1778         final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
   1779         String p = "<p style=\"height:" + dimension + "px;width:" + dimension + "px\">&nbsp;</p>";
   1780         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
   1781                 + "</body></html>", "text/html", null);
   1782         new PollingCheck() {
   1783             @Override
   1784             protected boolean check() {
   1785                 return mOnUiThread.getContentHeight() >= dimension;
   1786             }
   1787         }.run();
   1788 
   1789         int origX = mOnUiThread.getScrollX();
   1790         int origY = mOnUiThread.getScrollY();
   1791 
   1792         int half = dimension / 2;
   1793         Rect rect = new Rect(half, half, half + 1, half + 1);
   1794         assertTrue(mOnUiThread.requestChildRectangleOnScreen(mWebView, rect, true));
   1795         assertTrue(mOnUiThread.getScrollX() > origX);
   1796         assertTrue(mOnUiThread.getScrollY() > origY);
   1797     }
   1798 
   1799     public void testSetDownloadListener() throws Throwable {
   1800         final class MyDownloadListener implements DownloadListener {
   1801             public String url;
   1802             public String mimeType;
   1803             public long contentLength;
   1804             public String contentDisposition;
   1805             public boolean called;
   1806 
   1807             @Override
   1808             public void onDownloadStart(String url, String userAgent, String contentDisposition,
   1809                     String mimetype, long contentLength) {
   1810                 this.url = url;
   1811                 this.mimeType = mimetype;
   1812                 this.contentLength = contentLength;
   1813                 this.contentDisposition = contentDisposition;
   1814                 this.called = true;
   1815             }
   1816         }
   1817 
   1818         final String mimeType = "application/octet-stream";
   1819         final int length = 100;
   1820         final MyDownloadListener listener = new MyDownloadListener();
   1821 
   1822         startWebServer(false);
   1823         final String url = mWebServer.getBinaryUrl(mimeType, length);
   1824 
   1825         // By default, WebView sends an intent to ask the system to
   1826         // handle loading a new URL. We set WebViewClient as
   1827         // WebViewClient.shouldOverrideUrlLoading() returns false, so
   1828         // the WebView will load the new URL.
   1829         mOnUiThread.setDownloadListener(listener);
   1830         mOnUiThread.getSettings().setJavaScriptEnabled(true);
   1831 
   1832         // See b/13675265 for discussion on why the setTimeout is necessary.
   1833         // Works around a Blink bug.
   1834         mOnUiThread.loadDataAndWaitForCompletion(
   1835                 "<html><body onload=\"setTimeout(" +
   1836                 "function() { window.location = \'" + url + "\'; }, 100);\">" +
   1837                 "</body></html>", "text/html", null);
   1838         // Wait for layout to complete before setting focus.
   1839         getInstrumentation().waitForIdleSync();
   1840 
   1841         new PollingCheck(TEST_TIMEOUT) {
   1842             @Override
   1843             protected boolean check() {
   1844                 return listener.called;
   1845             }
   1846         }.run();
   1847         assertEquals(url, listener.url);
   1848         assertTrue(listener.contentDisposition.contains("test.bin"));
   1849         // ToBeFixed: uncomment the following tests after fixing the framework
   1850         // assertEquals(mimeType, listener.mimeType);
   1851         // assertEquals(length, listener.contentLength);
   1852     }
   1853 
   1854     @UiThreadTest
   1855     public void testSetLayoutParams() {
   1856         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(600, 800);
   1857         mWebView.setLayoutParams(params);
   1858         assertSame(params, mWebView.getLayoutParams());
   1859     }
   1860 
   1861     @UiThreadTest
   1862     public void testSetMapTrackballToArrowKeys() {
   1863         mWebView.setMapTrackballToArrowKeys(true);
   1864     }
   1865 
   1866     public void testSetNetworkAvailable() throws Exception {
   1867         WebSettings settings = mOnUiThread.getSettings();
   1868         settings.setJavaScriptEnabled(true);
   1869         startWebServer(false);
   1870 
   1871         String url = mWebServer.getAssetUrl(TestHtmlConstants.NETWORK_STATE_URL);
   1872         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1873         assertEquals("ONLINE", mOnUiThread.getTitle());
   1874 
   1875         mOnUiThread.setNetworkAvailable(false);
   1876 
   1877         // Wait for the DOM to receive notification of the network state change.
   1878         new PollingCheck(TEST_TIMEOUT) {
   1879             @Override
   1880             protected boolean check() {
   1881                 return mOnUiThread.getTitle().equals("OFFLINE");
   1882             }
   1883         }.run();
   1884 
   1885         mOnUiThread.setNetworkAvailable(true);
   1886 
   1887         // Wait for the DOM to receive notification of the network state change.
   1888         new PollingCheck(TEST_TIMEOUT) {
   1889             @Override
   1890             protected boolean check() {
   1891                 return mOnUiThread.getTitle().equals("ONLINE");
   1892             }
   1893         }.run();
   1894     }
   1895 
   1896     public void testSetWebChromeClient() throws Throwable {
   1897         final class MockWebChromeClient extends WaitForProgressClient {
   1898             private boolean mOnProgressChanged = false;
   1899 
   1900             public MockWebChromeClient() {
   1901                 super(mOnUiThread);
   1902             }
   1903 
   1904             @Override
   1905             public void onProgressChanged(WebView view, int newProgress) {
   1906                 super.onProgressChanged(view, newProgress);
   1907                 mOnProgressChanged = true;
   1908             }
   1909             public boolean onProgressChangedCalled() {
   1910                 return mOnProgressChanged;
   1911             }
   1912         }
   1913 
   1914         final MockWebChromeClient webChromeClient = new MockWebChromeClient();
   1915 
   1916         mOnUiThread.setWebChromeClient(webChromeClient);
   1917         getInstrumentation().waitForIdleSync();
   1918         assertFalse(webChromeClient.onProgressChangedCalled());
   1919 
   1920         startWebServer(false);
   1921         final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
   1922         mOnUiThread.loadUrlAndWaitForCompletion(url);
   1923         getInstrumentation().waitForIdleSync();
   1924 
   1925         new PollingCheck(TEST_TIMEOUT) {
   1926             @Override
   1927             protected boolean check() {
   1928                 return webChromeClient.onProgressChangedCalled();
   1929             }
   1930         }.run();
   1931     }
   1932 
   1933     public void testPauseResumeTimers() throws Throwable {
   1934         class Monitor {
   1935             private boolean mIsUpdated;
   1936 
   1937             @JavascriptInterface
   1938             public synchronized void update() {
   1939                 mIsUpdated  = true;
   1940                 notify();
   1941             }
   1942             public synchronized boolean waitForUpdate() {
   1943                 while (!mIsUpdated) {
   1944                     try {
   1945                         // This is slightly flaky, as we can't guarantee that
   1946                         // this is a sufficient time limit, but there's no way
   1947                         // around this.
   1948                         wait(1000);
   1949                         if (!mIsUpdated) {
   1950                             return false;
   1951                         }
   1952                     } catch (InterruptedException e) {
   1953                     }
   1954                 }
   1955                 mIsUpdated = false;
   1956                 return true;
   1957             }
   1958         };
   1959         final Monitor monitor = new Monitor();
   1960         final String updateMonitorHtml = "<html>" +
   1961                 "<body onload=\"monitor.update();\"></body></html>";
   1962 
   1963         // Test that JavaScript is executed even with timers paused.
   1964         runTestOnUiThread(new Runnable() {
   1965             @Override
   1966             public void run() {
   1967                 mWebView.getSettings().setJavaScriptEnabled(true);
   1968                 mWebView.addJavascriptInterface(monitor, "monitor");
   1969                 mWebView.pauseTimers();
   1970                 mOnUiThread.loadDataAndWaitForCompletion(updateMonitorHtml,
   1971                         "text/html", null);
   1972             }
   1973         });
   1974         assertTrue(monitor.waitForUpdate());
   1975 
   1976         // Start a timer and test that it does not fire.
   1977         mOnUiThread.loadDataAndWaitForCompletion(
   1978                 "<html><body onload='setTimeout(function(){monitor.update();},100)'>" +
   1979                 "</body></html>", "text/html", null);
   1980         assertFalse(monitor.waitForUpdate());
   1981 
   1982         // Resume timers and test that the timer fires.
   1983         mOnUiThread.resumeTimers();
   1984         assertTrue(monitor.waitForUpdate());
   1985     }
   1986 
   1987     // verify query parameters can be passed correctly to android asset files
   1988     public void testAndroidAssetQueryParam() {
   1989 
   1990         WebSettings settings = mOnUiThread.getSettings();
   1991         settings.setJavaScriptEnabled(true);
   1992         // test passing a parameter
   1993         String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.PARAM_ASSET_URL+"?val=SUCCESS");
   1994         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
   1995         assertEquals("SUCCESS", mOnUiThread.getTitle());
   1996     }
   1997 
   1998     // verify anchors work correctly for android asset files
   1999     public void testAndroidAssetAnchor() {
   2000 
   2001         WebSettings settings = mOnUiThread.getSettings();
   2002         settings.setJavaScriptEnabled(true);
   2003         // test using an anchor
   2004         String fileUrl = TestHtmlConstants.getFileUrl(TestHtmlConstants.ANCHOR_ASSET_URL+"#anchor");
   2005         mOnUiThread.loadUrlAndWaitForCompletion(fileUrl);
   2006         assertEquals("anchor", mOnUiThread.getTitle());
   2007     }
   2008 
   2009     public void testEvaluateJavascript() {
   2010         mOnUiThread.getSettings().setJavaScriptEnabled(true);
   2011         mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
   2012 
   2013         EvaluateJsResultPollingCheck jsResult = new EvaluateJsResultPollingCheck("2");
   2014         mOnUiThread.evaluateJavascript("1+1", jsResult);
   2015         jsResult.run();
   2016 
   2017         jsResult = new EvaluateJsResultPollingCheck("9");
   2018         mOnUiThread.evaluateJavascript("1+1; 4+5", jsResult);
   2019         jsResult.run();
   2020 
   2021         final String EXPECTED_TITLE = "test";
   2022         mOnUiThread.evaluateJavascript("document.title='" + EXPECTED_TITLE + "';", null);
   2023         new PollingCheck(TEST_TIMEOUT) {
   2024             @Override
   2025             protected boolean check() {
   2026                 return mOnUiThread.getTitle().equals(EXPECTED_TITLE);
   2027             }
   2028         }.run();
   2029     }
   2030 
   2031     // Verify Print feature can create a PDF file with a correct preamble.
   2032     public void testPrinting() throws Throwable {
   2033         mOnUiThread.loadDataAndWaitForCompletion("<html><head></head>" +
   2034                 "<body>foo</body></html>",
   2035                 "text/html", null);
   2036         final PrintDocumentAdapter adapter =  mOnUiThread.createPrintDocumentAdapter();
   2037         printDocumentStart(adapter);
   2038         PrintAttributes attributes = new PrintAttributes.Builder()
   2039                 .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
   2040                 .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
   2041                 .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
   2042                 .build();
   2043         final WebViewStubActivity activity = getActivity();
   2044         final File file = activity.getFileStreamPath(PRINTER_TEST_FILE);
   2045         final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file,
   2046                 ParcelFileDescriptor.parseMode("w"));
   2047         final FutureTask<Boolean> result =
   2048                 new FutureTask<Boolean>(new Callable<Boolean>() {
   2049                             public Boolean call() {
   2050                                 return true;
   2051                             }
   2052                         });
   2053         printDocumentLayout(adapter, null, attributes,
   2054                 new LayoutResultCallback() {
   2055                     // Called on UI thread
   2056                     @Override
   2057                     public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
   2058                         savePrintedPage(adapter, descriptor, result);
   2059                     }
   2060                 });
   2061         try {
   2062             result.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
   2063             assertTrue(file.length() > 0);
   2064             FileInputStream in = new FileInputStream(file);
   2065             byte[] b = new byte[PDF_PREAMBLE.length()];
   2066             in.read(b);
   2067             String preamble = new String(b);
   2068             assertEquals(PDF_PREAMBLE, preamble);
   2069         } finally {
   2070             // close the descriptor, if not closed already.
   2071             descriptor.close();
   2072             file.delete();
   2073         }
   2074     }
   2075 
   2076     private void savePrintedPage(final PrintDocumentAdapter adapter,
   2077             final ParcelFileDescriptor descriptor, final FutureTask<Boolean> result) {
   2078         adapter.onWrite(new PageRange[] {PageRange.ALL_PAGES}, descriptor,
   2079                 new CancellationSignal(),
   2080                 new WriteResultCallback() {
   2081                     @Override
   2082                     public void onWriteFinished(PageRange[] pages) {
   2083                         try {
   2084                             descriptor.close();
   2085                             result.run();
   2086                         } catch (IOException ex) {
   2087                             fail("Failed file operation: " + ex.toString());
   2088                         }
   2089                     }
   2090                 });
   2091     }
   2092 
   2093     private void printDocumentStart(final PrintDocumentAdapter adapter) {
   2094         mOnUiThread.runOnUiThread(new Runnable() {
   2095             @Override
   2096             public void run() {
   2097                 adapter.onStart();
   2098             }
   2099         });
   2100     }
   2101 
   2102     private void printDocumentLayout(final PrintDocumentAdapter adapter,
   2103             final PrintAttributes oldAttributes, final PrintAttributes newAttributes,
   2104             final LayoutResultCallback layoutResultCallback) {
   2105         mOnUiThread.runOnUiThread(new Runnable() {
   2106             @Override
   2107             public void run() {
   2108                 adapter.onLayout(oldAttributes, newAttributes, new CancellationSignal(),
   2109                         layoutResultCallback, null);
   2110             }
   2111         });
   2112     }
   2113 
   2114     @UiThreadTest
   2115     public void testInternals() {
   2116         // Do not test these APIs. They are implementation details.
   2117     }
   2118 
   2119     private static class HrefCheckHandler extends Handler {
   2120         private boolean mHadRecieved;
   2121 
   2122         private String mResultUrl;
   2123 
   2124         public HrefCheckHandler(Looper looper) {
   2125             super(looper);
   2126         }
   2127 
   2128         public boolean hasCalledHandleMessage() {
   2129             return mHadRecieved;
   2130         }
   2131 
   2132         public String getResultUrl() {
   2133             return mResultUrl;
   2134         }
   2135 
   2136         public void reset(){
   2137             mResultUrl = null;
   2138             mHadRecieved = false;
   2139         }
   2140 
   2141         @Override
   2142         public void handleMessage(Message msg) {
   2143             mResultUrl = msg.getData().getString("url");
   2144             mHadRecieved = true;
   2145         }
   2146     }
   2147 
   2148     private void moveFocusDown() throws Throwable {
   2149         // send down key and wait for idle
   2150         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
   2151         // waiting for idle isn't always sufficient for the key to be fully processed
   2152         Thread.sleep(500);
   2153     }
   2154 
   2155     private void pollingCheckWebBackForwardList(final String currUrl, final int currIndex,
   2156             final int size) {
   2157         new PollingCheck() {
   2158             @Override
   2159             protected boolean check() {
   2160                 WebBackForwardList list = mWebView.copyBackForwardList();
   2161                 return checkWebBackForwardList(list, currUrl, currIndex, size);
   2162             }
   2163         }.run();
   2164     }
   2165 
   2166     private boolean checkWebBackForwardList(WebBackForwardList list, String currUrl,
   2167             int currIndex, int size) {
   2168         return (list != null)
   2169                 && (list.getSize() == size)
   2170                 && (list.getCurrentIndex() == currIndex)
   2171                 && list.getItemAtIndex(currIndex).getUrl().equals(currUrl);
   2172     }
   2173 
   2174     private void assertGoBackOrForwardBySteps(boolean expected, int steps) {
   2175         // skip if steps equals to 0
   2176         if (steps == 0)
   2177             return;
   2178 
   2179         int start = steps > 0 ? 1 : steps;
   2180         int end = steps > 0 ? steps : -1;
   2181 
   2182         // check all the steps in the history
   2183         for (int i = start; i <= end; i++) {
   2184             assertEquals(expected, mWebView.canGoBackOrForward(i));
   2185 
   2186             // shortcut methods for one step
   2187             if (i == 1) {
   2188                 assertEquals(expected, mWebView.canGoForward());
   2189             } else if (i == -1) {
   2190                 assertEquals(expected, mWebView.canGoBack());
   2191             }
   2192         }
   2193     }
   2194 
   2195     private boolean isPictureFilledWithColor(Picture picture, int color) {
   2196         if (picture.getWidth() == 0 || picture.getHeight() == 0)
   2197             return false;
   2198 
   2199         Bitmap bitmap = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(),
   2200                 Config.ARGB_8888);
   2201         picture.draw(new Canvas(bitmap));
   2202 
   2203         for (int i = 0; i < bitmap.getWidth(); i ++) {
   2204             for (int j = 0; j < bitmap.getHeight(); j ++) {
   2205                 if (color != bitmap.getPixel(i, j)) {
   2206                     return false;
   2207                 }
   2208             }
   2209         }
   2210         return true;
   2211     }
   2212 
   2213     // Find b1 inside b2
   2214     private boolean checkBitmapInsideAnother(Bitmap b1, Bitmap b2) {
   2215         int w = b1.getWidth();
   2216         int h = b1.getHeight();
   2217 
   2218         for (int i = 0; i < (b2.getWidth()-w+1); i++) {
   2219             for (int j = 0; j < (b2.getHeight()-h+1); j++) {
   2220                 if (checkBitmapInsideAnother(b1, b2, i, j))
   2221                     return true;
   2222             }
   2223         }
   2224         return false;
   2225     }
   2226 
   2227     private boolean comparePixel(int p1, int p2, int maxError) {
   2228         int err;
   2229         err = Math.abs(((p1&0xff000000)>>>24) - ((p2&0xff000000)>>>24));
   2230         if (err > maxError)
   2231             return false;
   2232 
   2233         err = Math.abs(((p1&0x00ff0000)>>>16) - ((p2&0x00ff0000)>>>16));
   2234         if (err > maxError)
   2235             return false;
   2236 
   2237         err = Math.abs(((p1&0x0000ff00)>>>8) - ((p2&0x0000ff00)>>>8));
   2238         if (err > maxError)
   2239             return false;
   2240 
   2241         err = Math.abs(((p1&0x000000ff)>>>0) - ((p2&0x000000ff)>>>0));
   2242         if (err > maxError)
   2243             return false;
   2244 
   2245         return true;
   2246     }
   2247 
   2248     private boolean checkBitmapInsideAnother(Bitmap b1, Bitmap b2, int x, int y) {
   2249         for (int i = 0; i < b1.getWidth(); i++)
   2250             for (int j = 0; j < b1.getHeight(); j++) {
   2251                 if (!comparePixel(b1.getPixel(i, j), b2.getPixel(x + i, y + j), 10)) {
   2252                     return false;
   2253                 }
   2254             }
   2255         return true;
   2256     }
   2257 
   2258     /**
   2259      * Waits at least MIN_SCROLL_WAIT_MS for scrolling to start. Once started,
   2260      * scrolling is checked every SCROLL_WAIT_INTERVAL_MS for changes. Once
   2261      * changes have stopped, the function exits. If no scrolling has happened
   2262      * then the function exits after MIN_SCROLL_WAIT milliseconds.
   2263      * @param previousScrollY The Y scroll position prior to waiting.
   2264      */
   2265     private void waitForScrollingComplete(int previousScrollY)
   2266             throws InterruptedException {
   2267         int scrollY = previousScrollY;
   2268         // wait at least MIN_SCROLL_WAIT for something to happen.
   2269         long noChangeMinWait = SystemClock.uptimeMillis() + MIN_SCROLL_WAIT_MS;
   2270         boolean scrollChanging = false;
   2271         boolean scrollChanged = false;
   2272         boolean minWaitExpired = false;
   2273         while (scrollChanging || (!scrollChanged && !minWaitExpired)) {
   2274             Thread.sleep(SCROLL_WAIT_INTERVAL_MS);
   2275             int oldScrollY = scrollY;
   2276             scrollY = mOnUiThread.getScrollY();
   2277             scrollChanging = (scrollY != oldScrollY);
   2278             scrollChanged = (scrollY != previousScrollY);
   2279             minWaitExpired = (SystemClock.uptimeMillis() > noChangeMinWait);
   2280         }
   2281     }
   2282 
   2283     // Note that this class is not thread-safe.
   2284     final class SslErrorWebViewClient extends WaitForLoadedClient {
   2285         private boolean mWasOnReceivedSslErrorCalled;
   2286         private String mErrorUrl;
   2287 
   2288         public SslErrorWebViewClient() {
   2289             super(mOnUiThread);
   2290         }
   2291         @Override
   2292         public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
   2293             mWasOnReceivedSslErrorCalled = true;
   2294             mErrorUrl = error.getUrl();
   2295             handler.proceed();
   2296         }
   2297         public void resetWasOnReceivedSslErrorCalled() {
   2298             mWasOnReceivedSslErrorCalled = false;
   2299         }
   2300         public boolean wasOnReceivedSslErrorCalled() {
   2301             return mWasOnReceivedSslErrorCalled;
   2302         }
   2303         public String errorUrl() {
   2304             return mErrorUrl;
   2305         }
   2306     }
   2307 
   2308     final class ScaleChangedWebViewClient extends WaitForLoadedClient {
   2309         private boolean mOnScaleChangedCalled = false;
   2310         public ScaleChangedWebViewClient() {
   2311             super(mOnUiThread);
   2312         }
   2313 
   2314         @Override
   2315         public void onScaleChanged(WebView view, float oldScale, float newScale) {
   2316             super.onScaleChanged(view, oldScale, newScale);
   2317             synchronized (this) {
   2318                 mOnScaleChangedCalled = true;
   2319             }
   2320         }
   2321 
   2322         public void waitForScaleChanged() {
   2323             new PollingCheck(TEST_TIMEOUT) {
   2324                  @Override
   2325                  protected boolean check() {
   2326                      return onScaleChangedCalled();
   2327                  }
   2328             }.run();
   2329             synchronized (this) {
   2330                 mOnScaleChangedCalled = false;
   2331             }
   2332         }
   2333 
   2334         public synchronized boolean onScaleChangedCalled() {
   2335             return mOnScaleChangedCalled;
   2336         }
   2337     }
   2338 }
   2339