1 /* 2 * Copyright (C) 2011 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.graphics.Bitmap; 20 import android.graphics.Canvas; 21 import android.graphics.Picture; 22 import android.graphics.Rect; 23 import android.net.Uri; 24 import android.net.http.SslCertificate; 25 import android.os.Message; 26 import android.print.PrintDocumentAdapter; 27 import android.util.DisplayMetrics; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.view.ViewParent; 31 import android.webkit.CookieManager; 32 import android.webkit.DownloadListener; 33 import android.webkit.ValueCallback; 34 import android.webkit.WebBackForwardList; 35 import android.webkit.WebChromeClient; 36 import android.webkit.WebMessage; 37 import android.webkit.WebMessagePort; 38 import android.webkit.WebSettings; 39 import android.webkit.WebView; 40 import android.webkit.WebView.HitTestResult; 41 import android.webkit.WebView.PictureListener; 42 import android.webkit.WebView.VisualStateCallback; 43 import android.webkit.WebViewClient; 44 import android.webkit.WebViewRenderProcessClient; 45 46 import com.android.compatibility.common.util.PollingCheck; 47 48 import com.google.common.util.concurrent.SettableFuture; 49 50 import java.util.concurrent.Executor; 51 52 /** 53 * Many tests need to run WebView code in the UI thread. This class 54 * wraps a WebView so that calls are ensured to arrive on the UI thread. 55 * 56 * All methods may be run on either the UI thread or test thread. 57 * 58 * This should remain functionally equivalent to androidx.webkit.WebViewOnUiThread. 59 * Modifications to this class should be reflected in that class as necessary. See 60 * http://go/modifying-webview-cts. 61 */ 62 public class WebViewOnUiThread extends WebViewSyncLoader { 63 /** 64 * The WebView that calls will be made on. 65 */ 66 private WebView mWebView; 67 68 /** 69 * Wraps a WebView to ensure that methods are run on the UI thread. 70 * 71 * A new WebViewOnUiThread should be called during setUp so as to 72 * reinitialize between calls. 73 * 74 * @param webView The webView that the methods should call. 75 */ WebViewOnUiThread(WebView webView)76 public WebViewOnUiThread(WebView webView) { 77 super(webView); 78 mWebView = webView; 79 } 80 cleanUp()81 public void cleanUp() { 82 super.destroy(); 83 } 84 setWebViewClient(final WebViewClient webViewClient)85 public void setWebViewClient(final WebViewClient webViewClient) { 86 WebkitUtils.onMainThreadSync(() -> { 87 mWebView.setWebViewClient(webViewClient); 88 }); 89 } 90 setWebChromeClient(final WebChromeClient webChromeClient)91 public void setWebChromeClient(final WebChromeClient webChromeClient) { 92 WebkitUtils.onMainThreadSync(() -> { 93 mWebView.setWebChromeClient(webChromeClient); 94 }); 95 } 96 97 /** 98 * Set the webview renderer client for {@code mWebView}, on the UI thread. 99 */ setWebViewRenderProcessClient( final WebViewRenderProcessClient webViewRenderProcessClient)100 public void setWebViewRenderProcessClient( 101 final WebViewRenderProcessClient webViewRenderProcessClient) { 102 setWebViewRenderProcessClient(mWebView, webViewRenderProcessClient); 103 } 104 105 /** 106 * Set the webview renderer client for {@code webView}, on the UI thread. 107 */ setWebViewRenderProcessClient( final WebView webView, final WebViewRenderProcessClient webViewRenderProcessClient)108 public static void setWebViewRenderProcessClient( 109 final WebView webView, 110 final WebViewRenderProcessClient webViewRenderProcessClient) { 111 WebkitUtils.onMainThreadSync(() -> 112 webView.setWebViewRenderProcessClient(webViewRenderProcessClient) 113 ); 114 } 115 116 /** 117 * Set the webview renderer client for {@code mWebView}, on the UI thread, with callbacks 118 * executed by {@code executor} 119 */ setWebViewRenderProcessClient( final Executor executor, final WebViewRenderProcessClient webViewRenderProcessClient)120 public void setWebViewRenderProcessClient( 121 final Executor executor, final WebViewRenderProcessClient webViewRenderProcessClient) { 122 setWebViewRenderProcessClient(mWebView, executor, webViewRenderProcessClient); 123 } 124 125 /** 126 * Set the webview renderer client for {@code webView}, on the UI thread, with callbacks 127 * executed by {@code executor} 128 */ setWebViewRenderProcessClient( final WebView webView, final Executor executor, final WebViewRenderProcessClient webViewRenderProcessClient)129 public static void setWebViewRenderProcessClient( 130 final WebView webView, 131 final Executor executor, 132 final WebViewRenderProcessClient webViewRenderProcessClient) { 133 WebkitUtils.onMainThreadSync(() -> 134 webView.setWebViewRenderProcessClient(executor, webViewRenderProcessClient) 135 ); 136 } 137 138 /** 139 * Get the webview renderer client currently set on {@code mWebView}, on the UI thread. 140 */ getWebViewRenderProcessClient()141 public WebViewRenderProcessClient getWebViewRenderProcessClient() { 142 return getWebViewRenderProcessClient(mWebView); 143 } 144 145 /** 146 * Get the webview renderer client currently set on {@code webView}, on the UI thread. 147 */ getWebViewRenderProcessClient( final WebView webView)148 public static WebViewRenderProcessClient getWebViewRenderProcessClient( 149 final WebView webView) { 150 return WebkitUtils.onMainThreadSync(() -> { 151 return webView.getWebViewRenderProcessClient(); 152 }); 153 } 154 setPictureListener(final PictureListener pictureListener)155 public void setPictureListener(final PictureListener pictureListener) { 156 WebkitUtils.onMainThreadSync(() -> { 157 mWebView.setPictureListener(pictureListener); 158 }); 159 } 160 setNetworkAvailable(final boolean available)161 public void setNetworkAvailable(final boolean available) { 162 WebkitUtils.onMainThreadSync(() -> { 163 mWebView.setNetworkAvailable(available); 164 }); 165 } 166 setDownloadListener(final DownloadListener listener)167 public void setDownloadListener(final DownloadListener listener) { 168 WebkitUtils.onMainThreadSync(() -> { 169 mWebView.setDownloadListener(listener); 170 }); 171 } 172 setBackgroundColor(final int color)173 public void setBackgroundColor(final int color) { 174 WebkitUtils.onMainThreadSync(() -> { 175 mWebView.setBackgroundColor(color); 176 }); 177 } 178 clearCache(final boolean includeDiskFiles)179 public void clearCache(final boolean includeDiskFiles) { 180 WebkitUtils.onMainThreadSync(() -> { 181 mWebView.clearCache(includeDiskFiles); 182 }); 183 } 184 clearHistory()185 public void clearHistory() { 186 WebkitUtils.onMainThreadSync(() -> { 187 mWebView.clearHistory(); 188 }); 189 } 190 requestFocus()191 public void requestFocus() { 192 new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { 193 @Override 194 protected boolean check() { 195 requestFocusOnUiThread(); 196 return hasFocus(); 197 } 198 }.run(); 199 } 200 requestFocusOnUiThread()201 private void requestFocusOnUiThread() { 202 WebkitUtils.onMainThreadSync(() -> { 203 mWebView.requestFocus(); 204 }); 205 } 206 hasFocus()207 private boolean hasFocus() { 208 return WebkitUtils.onMainThreadSync(() -> { 209 return mWebView.hasFocus(); 210 }); 211 } 212 213 public boolean canZoomIn() { 214 return WebkitUtils.onMainThreadSync(() -> { 215 return mWebView.canZoomIn(); 216 }); 217 } 218 219 public boolean canZoomOut() { 220 return WebkitUtils.onMainThreadSync(() -> { 221 return mWebView.canZoomOut(); 222 }); 223 } 224 225 public boolean zoomIn() { 226 return WebkitUtils.onMainThreadSync(() -> { 227 return mWebView.zoomIn(); 228 }); 229 } 230 231 public boolean zoomOut() { 232 return WebkitUtils.onMainThreadSync(() -> { 233 return mWebView.zoomOut(); 234 }); 235 } 236 237 public void zoomBy(final float zoomFactor) { 238 WebkitUtils.onMainThreadSync(() -> { 239 mWebView.zoomBy(zoomFactor); 240 }); 241 } 242 243 public void setFindListener(final WebView.FindListener listener) { 244 WebkitUtils.onMainThreadSync(() -> { 245 mWebView.setFindListener(listener); 246 }); 247 } 248 249 public void removeJavascriptInterface(final String interfaceName) { 250 WebkitUtils.onMainThreadSync(() -> { 251 mWebView.removeJavascriptInterface(interfaceName); 252 }); 253 } 254 255 public WebMessagePort[] createWebMessageChannel() { 256 return WebkitUtils.onMainThreadSync(() -> { 257 return mWebView.createWebMessageChannel(); 258 }); 259 } 260 261 public void postWebMessage(final WebMessage message, final Uri targetOrigin) { 262 WebkitUtils.onMainThreadSync(() -> { 263 mWebView.postWebMessage(message, targetOrigin); 264 }); 265 } 266 267 public void addJavascriptInterface(final Object object, final String name) { 268 WebkitUtils.onMainThreadSync(() -> { 269 mWebView.addJavascriptInterface(object, name); 270 }); 271 } 272 273 public void flingScroll(final int vx, final int vy) { 274 WebkitUtils.onMainThreadSync(() -> { 275 mWebView.flingScroll(vx, vy); 276 }); 277 } 278 279 public void requestFocusNodeHref(final Message hrefMsg) { 280 WebkitUtils.onMainThreadSync(() -> { 281 mWebView.requestFocusNodeHref(hrefMsg); 282 }); 283 } 284 285 public void requestImageRef(final Message msg) { 286 WebkitUtils.onMainThreadSync(() -> { 287 mWebView.requestImageRef(msg); 288 }); 289 } 290 291 public void setInitialScale(final int scaleInPercent) { 292 WebkitUtils.onMainThreadSync(() -> { 293 mWebView.setInitialScale(scaleInPercent); 294 }); 295 } 296 297 public void clearSslPreferences() { 298 WebkitUtils.onMainThreadSync(() -> { 299 mWebView.clearSslPreferences(); 300 }); 301 } 302 303 public void clearClientCertPreferences(final Runnable onCleared) { 304 WebkitUtils.onMainThreadSync(() -> { 305 WebView.clearClientCertPreferences(onCleared); 306 }); 307 } 308 309 public void resumeTimers() { 310 WebkitUtils.onMainThreadSync(() -> { 311 mWebView.resumeTimers(); 312 }); 313 } 314 315 public void findNext(final boolean forward) { 316 WebkitUtils.onMainThreadSync(() -> { 317 mWebView.findNext(forward); 318 }); 319 } 320 321 public void clearMatches() { 322 WebkitUtils.onMainThreadSync(() -> { 323 mWebView.clearMatches(); 324 }); 325 } 326 327 public void loadUrl(final String url) { 328 WebkitUtils.onMainThreadSync(() -> { 329 mWebView.loadUrl(url); 330 }); 331 } 332 333 public void stopLoading() { 334 WebkitUtils.onMainThreadSync(() -> { 335 mWebView.stopLoading(); 336 }); 337 } 338 339 /** 340 * Reload the previous URL. 341 */ 342 public void reload() { 343 WebkitUtils.onMainThreadSync(() -> { 344 mWebView.reload(); 345 }); 346 } 347 348 public String getTitle() { 349 return WebkitUtils.onMainThreadSync(() -> { 350 return mWebView.getTitle(); 351 }); 352 } 353 354 public WebSettings getSettings() { 355 return WebkitUtils.onMainThreadSync(() -> { 356 return mWebView.getSettings(); 357 }); 358 } 359 360 public WebBackForwardList copyBackForwardList() { 361 return WebkitUtils.onMainThreadSync(() -> { 362 return mWebView.copyBackForwardList(); 363 }); 364 } 365 366 public Bitmap getFavicon() { 367 return WebkitUtils.onMainThreadSync(() -> { 368 return mWebView.getFavicon(); 369 }); 370 } 371 372 public String getUrl() { 373 return WebkitUtils.onMainThreadSync(() -> { 374 return mWebView.getUrl(); 375 }); 376 } 377 378 public int getProgress() { 379 return WebkitUtils.onMainThreadSync(() -> { 380 return mWebView.getProgress(); 381 }); 382 } 383 384 public int getHeight() { 385 return WebkitUtils.onMainThreadSync(() -> { 386 return mWebView.getHeight(); 387 }); 388 } 389 390 public int getContentHeight() { 391 return WebkitUtils.onMainThreadSync(() -> { 392 return mWebView.getContentHeight(); 393 }); 394 } 395 396 public boolean pageUp(final boolean top) { 397 return WebkitUtils.onMainThreadSync(() -> { 398 return mWebView.pageUp(top); 399 }); 400 } 401 402 public boolean pageDown(final boolean bottom) { 403 return WebkitUtils.onMainThreadSync(() -> { 404 return mWebView.pageDown(bottom); 405 }); 406 } 407 408 /** 409 * Post a visual state listener callback for mWebView on the UI thread. 410 */ 411 public void postVisualStateCallback(final long requestId, final VisualStateCallback callback) { 412 WebkitUtils.onMainThreadSync(() -> { 413 mWebView.postVisualStateCallback(requestId, callback); 414 }); 415 } 416 417 public int[] getLocationOnScreen() { 418 final int[] location = new int[2]; 419 return WebkitUtils.onMainThreadSync(() -> { 420 mWebView.getLocationOnScreen(location); 421 return location; 422 }); 423 } 424 425 public float getScale() { 426 return WebkitUtils.onMainThreadSync(() -> { 427 return mWebView.getScale(); 428 }); 429 } 430 431 public boolean requestFocus(final int direction, 432 final Rect previouslyFocusedRect) { 433 return WebkitUtils.onMainThreadSync(() -> { 434 return mWebView.requestFocus(direction, previouslyFocusedRect); 435 }); 436 } 437 438 public HitTestResult getHitTestResult() { 439 return WebkitUtils.onMainThreadSync(() -> { 440 return mWebView.getHitTestResult(); 441 }); 442 } 443 444 public int getScrollX() { 445 return WebkitUtils.onMainThreadSync(() -> { 446 return mWebView.getScrollX(); 447 }); 448 } 449 450 public int getScrollY() { 451 return WebkitUtils.onMainThreadSync(() -> { 452 return mWebView.getScrollY(); 453 }); 454 } 455 456 public final DisplayMetrics getDisplayMetrics() { 457 return WebkitUtils.onMainThreadSync(() -> { 458 return mWebView.getContext().getResources().getDisplayMetrics(); 459 }); 460 } 461 462 public boolean requestChildRectangleOnScreen(final View child, 463 final Rect rect, 464 final boolean immediate) { 465 return WebkitUtils.onMainThreadSync(() -> { 466 return mWebView.requestChildRectangleOnScreen(child, rect, 467 immediate); 468 }); 469 } 470 471 public int findAll(final String find) { 472 return WebkitUtils.onMainThreadSync(() -> { 473 return mWebView.findAll(find); 474 }); 475 } 476 477 public Picture capturePicture() { 478 return WebkitUtils.onMainThreadSync(() -> { 479 return mWebView.capturePicture(); 480 }); 481 } 482 483 /** 484 * Execute javascript synchronously, returning the result. 485 */ 486 public String evaluateJavascriptSync(final String script) { 487 final SettableFuture<String> future = SettableFuture.create(); 488 evaluateJavascript(script, result -> future.set(result)); 489 return WebkitUtils.waitForFuture(future); 490 } 491 492 public void evaluateJavascript(final String script, final ValueCallback<String> result) { 493 WebkitUtils.onMainThreadSync(() -> { 494 mWebView.evaluateJavascript(script, result); 495 }); 496 } 497 498 public void saveWebArchive(final String basename, final boolean autoname, 499 final ValueCallback<String> callback) { 500 WebkitUtils.onMainThreadSync(() -> { 501 mWebView.saveWebArchive(basename, autoname, callback); 502 }); 503 } 504 505 public SslCertificate getCertificate() { 506 return WebkitUtils.onMainThreadSync(() -> { 507 return mWebView.getCertificate(); 508 }); 509 } 510 511 public WebView createWebView() { 512 return WebkitUtils.onMainThreadSync(() -> { 513 return new WebView(mWebView.getContext()); 514 }); 515 } 516 517 public PrintDocumentAdapter createPrintDocumentAdapter() { 518 return WebkitUtils.onMainThreadSync(() -> { 519 return mWebView.createPrintDocumentAdapter(); 520 }); 521 } 522 523 public void setLayoutHeightToMatchParent() { 524 WebkitUtils.onMainThreadSync(() -> { 525 ViewParent parent = mWebView.getParent(); 526 if (parent instanceof ViewGroup) { 527 ((ViewGroup) parent).getLayoutParams().height = 528 ViewGroup.LayoutParams.MATCH_PARENT; 529 } 530 mWebView.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; 531 mWebView.requestLayout(); 532 }); 533 } 534 535 public void setLayoutToMatchParent() { 536 WebkitUtils.onMainThreadSync(() -> { 537 setMatchParent((View) mWebView.getParent()); 538 setMatchParent(mWebView); 539 mWebView.requestLayout(); 540 }); 541 } 542 543 public void setAcceptThirdPartyCookies(final boolean accept) { 544 WebkitUtils.onMainThreadSync(() -> { 545 CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, accept); 546 }); 547 } 548 549 public boolean acceptThirdPartyCookies() { 550 return WebkitUtils.onMainThreadSync(() -> { 551 return CookieManager.getInstance().acceptThirdPartyCookies(mWebView); 552 }); 553 } 554 555 /** 556 * Accessor for underlying WebView. 557 * @return The WebView being wrapped by this class. 558 */ 559 public WebView getWebView() { 560 return mWebView; 561 } 562 563 /** 564 * Wait for the current state of the DOM to be ready to render on the next draw. 565 */ 566 public void waitForDOMReadyToRender() { 567 final SettableFuture<Void> future = SettableFuture.create(); 568 postVisualStateCallback(0, new VisualStateCallback() { 569 @Override 570 public void onComplete(long requestId) { 571 future.set(null); 572 } 573 }); 574 WebkitUtils.waitForFuture(future); 575 } 576 577 /** 578 * Capture a bitmap representation of the current WebView state. 579 * 580 * This synchronises so that the bitmap contents reflects the current DOM state, rather than 581 * potentially capturing a previously generated frame. 582 */ 583 public Bitmap captureBitmap() { 584 getSettings().setOffscreenPreRaster(true); 585 waitForDOMReadyToRender(); 586 return WebkitUtils.onMainThreadSync(() -> { 587 Bitmap bitmap = Bitmap.createBitmap(mWebView.getWidth(), mWebView.getHeight(), 588 Bitmap.Config.ARGB_8888); 589 Canvas canvas = new Canvas(bitmap); 590 mWebView.draw(canvas); 591 return bitmap; 592 }); 593 } 594 595 /** 596 * Set LayoutParams to MATCH_PARENT. 597 * 598 * @param view Target view 599 */ 600 private void setMatchParent(View view) { 601 ViewGroup.LayoutParams params = view.getLayoutParams(); 602 params.height = ViewGroup.LayoutParams.MATCH_PARENT; 603 params.width = ViewGroup.LayoutParams.MATCH_PARENT; 604 view.setLayoutParams(params); 605 } 606 } 607