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 * The caller is responsible for destroying the WebView instance. 75 * 76 * @param webView The webView that the methods should call. 77 */ WebViewOnUiThread(WebView webView)78 public WebViewOnUiThread(WebView webView) { 79 super(webView); 80 mWebView = webView; 81 } 82 cleanUp()83 public void cleanUp() { 84 super.destroy(); 85 } 86 setWebViewClient(final WebViewClient webViewClient)87 public void setWebViewClient(final WebViewClient webViewClient) { 88 WebkitUtils.onMainThreadSync(() -> { 89 mWebView.setWebViewClient(webViewClient); 90 }); 91 } 92 setWebChromeClient(final WebChromeClient webChromeClient)93 public void setWebChromeClient(final WebChromeClient webChromeClient) { 94 WebkitUtils.onMainThreadSync(() -> { 95 mWebView.setWebChromeClient(webChromeClient); 96 }); 97 } 98 99 /** 100 * Set the webview renderer client for {@code mWebView}, on the UI thread. 101 */ setWebViewRenderProcessClient( final WebViewRenderProcessClient webViewRenderProcessClient)102 public void setWebViewRenderProcessClient( 103 final WebViewRenderProcessClient webViewRenderProcessClient) { 104 setWebViewRenderProcessClient(mWebView, webViewRenderProcessClient); 105 } 106 107 /** 108 * Set the webview renderer client for {@code webView}, on the UI thread. 109 */ setWebViewRenderProcessClient( final WebView webView, final WebViewRenderProcessClient webViewRenderProcessClient)110 public static void setWebViewRenderProcessClient( 111 final WebView webView, 112 final WebViewRenderProcessClient webViewRenderProcessClient) { 113 WebkitUtils.onMainThreadSync(() -> 114 webView.setWebViewRenderProcessClient(webViewRenderProcessClient) 115 ); 116 } 117 118 /** 119 * Set the webview renderer client for {@code mWebView}, on the UI thread, with callbacks 120 * executed by {@code executor} 121 */ setWebViewRenderProcessClient( final Executor executor, final WebViewRenderProcessClient webViewRenderProcessClient)122 public void setWebViewRenderProcessClient( 123 final Executor executor, final WebViewRenderProcessClient webViewRenderProcessClient) { 124 setWebViewRenderProcessClient(mWebView, executor, webViewRenderProcessClient); 125 } 126 127 /** 128 * Set the webview renderer client for {@code webView}, on the UI thread, with callbacks 129 * executed by {@code executor} 130 */ setWebViewRenderProcessClient( final WebView webView, final Executor executor, final WebViewRenderProcessClient webViewRenderProcessClient)131 public static void setWebViewRenderProcessClient( 132 final WebView webView, 133 final Executor executor, 134 final WebViewRenderProcessClient webViewRenderProcessClient) { 135 WebkitUtils.onMainThreadSync(() -> 136 webView.setWebViewRenderProcessClient(executor, webViewRenderProcessClient) 137 ); 138 } 139 140 /** 141 * Get the webview renderer client currently set on {@code mWebView}, on the UI thread. 142 */ getWebViewRenderProcessClient()143 public WebViewRenderProcessClient getWebViewRenderProcessClient() { 144 return getWebViewRenderProcessClient(mWebView); 145 } 146 147 /** 148 * Get the webview renderer client currently set on {@code webView}, on the UI thread. 149 */ getWebViewRenderProcessClient( final WebView webView)150 public static WebViewRenderProcessClient getWebViewRenderProcessClient( 151 final WebView webView) { 152 return WebkitUtils.onMainThreadSync(() -> { 153 return webView.getWebViewRenderProcessClient(); 154 }); 155 } 156 setPictureListener(final PictureListener pictureListener)157 public void setPictureListener(final PictureListener pictureListener) { 158 WebkitUtils.onMainThreadSync(() -> { 159 mWebView.setPictureListener(pictureListener); 160 }); 161 } 162 setNetworkAvailable(final boolean available)163 public void setNetworkAvailable(final boolean available) { 164 WebkitUtils.onMainThreadSync(() -> { 165 mWebView.setNetworkAvailable(available); 166 }); 167 } 168 setDownloadListener(final DownloadListener listener)169 public void setDownloadListener(final DownloadListener listener) { 170 WebkitUtils.onMainThreadSync(() -> { 171 mWebView.setDownloadListener(listener); 172 }); 173 } 174 setBackgroundColor(final int color)175 public void setBackgroundColor(final int color) { 176 WebkitUtils.onMainThreadSync(() -> { 177 mWebView.setBackgroundColor(color); 178 }); 179 } 180 clearCache(final boolean includeDiskFiles)181 public void clearCache(final boolean includeDiskFiles) { 182 WebkitUtils.onMainThreadSync(() -> { 183 mWebView.clearCache(includeDiskFiles); 184 }); 185 } 186 clearHistory()187 public void clearHistory() { 188 WebkitUtils.onMainThreadSync(() -> { 189 mWebView.clearHistory(); 190 }); 191 } 192 requestFocus()193 public void requestFocus() { 194 new PollingCheck(WebkitUtils.TEST_TIMEOUT_MS) { 195 @Override 196 protected boolean check() { 197 requestFocusOnUiThread(); 198 return hasFocus(); 199 } 200 }.run(); 201 } 202 requestFocusOnUiThread()203 private void requestFocusOnUiThread() { 204 WebkitUtils.onMainThreadSync(() -> { 205 mWebView.requestFocus(); 206 }); 207 } 208 hasFocus()209 private boolean hasFocus() { 210 return WebkitUtils.onMainThreadSync(() -> { 211 return mWebView.hasFocus(); 212 }); 213 } 214 215 public boolean canZoomIn() { 216 return WebkitUtils.onMainThreadSync(() -> { 217 return mWebView.canZoomIn(); 218 }); 219 } 220 221 public boolean canZoomOut() { 222 return WebkitUtils.onMainThreadSync(() -> { 223 return mWebView.canZoomOut(); 224 }); 225 } 226 227 public boolean zoomIn() { 228 return WebkitUtils.onMainThreadSync(() -> { 229 return mWebView.zoomIn(); 230 }); 231 } 232 233 public boolean zoomOut() { 234 return WebkitUtils.onMainThreadSync(() -> { 235 return mWebView.zoomOut(); 236 }); 237 } 238 239 public void zoomBy(final float zoomFactor) { 240 WebkitUtils.onMainThreadSync(() -> { 241 mWebView.zoomBy(zoomFactor); 242 }); 243 } 244 245 public void setFindListener(final WebView.FindListener listener) { 246 WebkitUtils.onMainThreadSync(() -> { 247 mWebView.setFindListener(listener); 248 }); 249 } 250 251 public void removeJavascriptInterface(final String interfaceName) { 252 WebkitUtils.onMainThreadSync(() -> { 253 mWebView.removeJavascriptInterface(interfaceName); 254 }); 255 } 256 257 public WebMessagePort[] createWebMessageChannel() { 258 return WebkitUtils.onMainThreadSync(() -> { 259 return mWebView.createWebMessageChannel(); 260 }); 261 } 262 263 public void postWebMessage(final WebMessage message, final Uri targetOrigin) { 264 WebkitUtils.onMainThreadSync(() -> { 265 mWebView.postWebMessage(message, targetOrigin); 266 }); 267 } 268 269 public void addJavascriptInterface(final Object object, final String name) { 270 WebkitUtils.onMainThreadSync(() -> { 271 mWebView.addJavascriptInterface(object, name); 272 }); 273 } 274 275 public void flingScroll(final int vx, final int vy) { 276 WebkitUtils.onMainThreadSync(() -> { 277 mWebView.flingScroll(vx, vy); 278 }); 279 } 280 281 public void requestFocusNodeHref(final Message hrefMsg) { 282 WebkitUtils.onMainThreadSync(() -> { 283 mWebView.requestFocusNodeHref(hrefMsg); 284 }); 285 } 286 287 public void requestImageRef(final Message msg) { 288 WebkitUtils.onMainThreadSync(() -> { 289 mWebView.requestImageRef(msg); 290 }); 291 } 292 293 public void setInitialScale(final int scaleInPercent) { 294 WebkitUtils.onMainThreadSync(() -> { 295 mWebView.setInitialScale(scaleInPercent); 296 }); 297 } 298 299 public void clearSslPreferences() { 300 WebkitUtils.onMainThreadSync(() -> { 301 mWebView.clearSslPreferences(); 302 }); 303 } 304 305 public void clearClientCertPreferences(final Runnable onCleared) { 306 WebkitUtils.onMainThreadSync(() -> { 307 WebView.clearClientCertPreferences(onCleared); 308 }); 309 } 310 311 public void resumeTimers() { 312 WebkitUtils.onMainThreadSync(() -> { 313 mWebView.resumeTimers(); 314 }); 315 } 316 317 public void findNext(final boolean forward) { 318 WebkitUtils.onMainThreadSync(() -> { 319 mWebView.findNext(forward); 320 }); 321 } 322 323 public void clearMatches() { 324 WebkitUtils.onMainThreadSync(() -> { 325 mWebView.clearMatches(); 326 }); 327 } 328 329 public void loadUrl(final String url) { 330 WebkitUtils.onMainThreadSync(() -> { 331 mWebView.loadUrl(url); 332 }); 333 } 334 335 public void stopLoading() { 336 WebkitUtils.onMainThreadSync(() -> { 337 mWebView.stopLoading(); 338 }); 339 } 340 341 /** 342 * Reload the previous URL. 343 */ 344 public void reload() { 345 WebkitUtils.onMainThreadSync(() -> { 346 mWebView.reload(); 347 }); 348 } 349 350 public String getTitle() { 351 return WebkitUtils.onMainThreadSync(() -> { 352 return mWebView.getTitle(); 353 }); 354 } 355 356 public WebSettings getSettings() { 357 return WebkitUtils.onMainThreadSync(() -> { 358 return mWebView.getSettings(); 359 }); 360 } 361 362 public WebBackForwardList copyBackForwardList() { 363 return WebkitUtils.onMainThreadSync(() -> { 364 return mWebView.copyBackForwardList(); 365 }); 366 } 367 368 public Bitmap getFavicon() { 369 return WebkitUtils.onMainThreadSync(() -> { 370 return mWebView.getFavicon(); 371 }); 372 } 373 374 public String getUrl() { 375 return WebkitUtils.onMainThreadSync(() -> { 376 return mWebView.getUrl(); 377 }); 378 } 379 380 public int getProgress() { 381 return WebkitUtils.onMainThreadSync(() -> { 382 return mWebView.getProgress(); 383 }); 384 } 385 386 public int getHeight() { 387 return WebkitUtils.onMainThreadSync(() -> { 388 return mWebView.getHeight(); 389 }); 390 } 391 392 public int getContentHeight() { 393 return WebkitUtils.onMainThreadSync(() -> { 394 return mWebView.getContentHeight(); 395 }); 396 } 397 398 public boolean pageUp(final boolean top) { 399 return WebkitUtils.onMainThreadSync(() -> { 400 return mWebView.pageUp(top); 401 }); 402 } 403 404 public boolean pageDown(final boolean bottom) { 405 return WebkitUtils.onMainThreadSync(() -> { 406 return mWebView.pageDown(bottom); 407 }); 408 } 409 410 /** 411 * Post a visual state listener callback for mWebView on the UI thread. 412 */ 413 public void postVisualStateCallback(final long requestId, final VisualStateCallback callback) { 414 WebkitUtils.onMainThreadSync(() -> { 415 mWebView.postVisualStateCallback(requestId, callback); 416 }); 417 } 418 419 public int[] getLocationOnScreen() { 420 final int[] location = new int[2]; 421 return WebkitUtils.onMainThreadSync(() -> { 422 mWebView.getLocationOnScreen(location); 423 return location; 424 }); 425 } 426 427 public float getScale() { 428 return WebkitUtils.onMainThreadSync(() -> { 429 return mWebView.getScale(); 430 }); 431 } 432 433 public boolean requestFocus(final int direction, 434 final Rect previouslyFocusedRect) { 435 return WebkitUtils.onMainThreadSync(() -> { 436 return mWebView.requestFocus(direction, previouslyFocusedRect); 437 }); 438 } 439 440 public HitTestResult getHitTestResult() { 441 return WebkitUtils.onMainThreadSync(() -> { 442 return mWebView.getHitTestResult(); 443 }); 444 } 445 446 public int getScrollX() { 447 return WebkitUtils.onMainThreadSync(() -> { 448 return mWebView.getScrollX(); 449 }); 450 } 451 452 public int getScrollY() { 453 return WebkitUtils.onMainThreadSync(() -> { 454 return mWebView.getScrollY(); 455 }); 456 } 457 458 public final DisplayMetrics getDisplayMetrics() { 459 return WebkitUtils.onMainThreadSync(() -> { 460 return mWebView.getContext().getResources().getDisplayMetrics(); 461 }); 462 } 463 464 public boolean requestChildRectangleOnScreen(final View child, 465 final Rect rect, 466 final boolean immediate) { 467 return WebkitUtils.onMainThreadSync(() -> { 468 return mWebView.requestChildRectangleOnScreen(child, rect, 469 immediate); 470 }); 471 } 472 473 public int findAll(final String find) { 474 return WebkitUtils.onMainThreadSync(() -> { 475 return mWebView.findAll(find); 476 }); 477 } 478 479 public Picture capturePicture() { 480 return WebkitUtils.onMainThreadSync(() -> { 481 return mWebView.capturePicture(); 482 }); 483 } 484 485 /** 486 * Execute javascript synchronously, returning the result. 487 */ 488 public String evaluateJavascriptSync(final String script) { 489 final SettableFuture<String> future = SettableFuture.create(); 490 evaluateJavascript(script, result -> future.set(result)); 491 return WebkitUtils.waitForFuture(future); 492 } 493 494 public void evaluateJavascript(final String script, final ValueCallback<String> result) { 495 WebkitUtils.onMainThread(() -> mWebView.evaluateJavascript(script, result)); 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