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