WebViewChromium.java revision c0bac61af98d8597256114c3366cd23051ed2674
1/* 2 * Copyright (C) 2012 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 17package com.android.webview.chromium; 18 19import android.content.res.Configuration; 20import android.graphics.Bitmap; 21import android.graphics.Canvas; 22import android.graphics.Paint; 23import android.graphics.Picture; 24import android.graphics.Rect; 25import android.graphics.drawable.Drawable; 26import android.net.Uri; 27import android.net.http.SslCertificate; 28import android.os.Build; 29import android.os.Bundle; 30import android.os.Looper; 31import android.os.Message; 32import android.print.PrintDocumentAdapter; 33import android.text.TextUtils; 34import android.util.Base64; 35import android.util.Log; 36import android.view.HardwareCanvas; 37import android.view.KeyEvent; 38import android.view.MotionEvent; 39import android.view.View; 40import android.view.View.MeasureSpec; 41import android.view.ViewGroup; 42import android.view.accessibility.AccessibilityEvent; 43import android.view.accessibility.AccessibilityNodeInfo; 44import android.view.accessibility.AccessibilityNodeProvider; 45import android.view.inputmethod.EditorInfo; 46import android.view.inputmethod.InputConnection; 47import android.webkit.DownloadListener; 48import android.webkit.FindActionModeCallback; 49import android.webkit.JavascriptInterface; 50import android.webkit.ValueCallback; 51import android.webkit.WebBackForwardList; 52import android.webkit.WebChromeClient; 53import android.webkit.WebSettings; 54import android.webkit.WebView; 55import android.webkit.WebViewClient; 56import android.webkit.WebViewProvider; 57import android.widget.TextView; 58 59import org.chromium.android_webview.AwBrowserContext; 60import org.chromium.android_webview.AwContents; 61import org.chromium.android_webview.AwLayoutSizer; 62import org.chromium.android_webview.AwSettings; 63import org.chromium.android_webview.AwPrintDocumentAdapter; 64import org.chromium.base.ThreadUtils; 65import org.chromium.content.browser.LoadUrlParams; 66import org.chromium.net.NetworkChangeNotifier; 67 68import java.io.BufferedWriter; 69import java.io.File; 70import java.lang.annotation.Annotation; 71import java.util.concurrent.Callable; 72import java.util.concurrent.ConcurrentLinkedQueue; 73import java.util.concurrent.FutureTask; 74import java.util.concurrent.TimeUnit; 75import java.util.HashMap; 76import java.util.Map; 77import java.util.Queue; 78 79/** 80 * This class is the delegate to which WebViewProxy forwards all API calls. 81 * 82 * Most of the actual functionality is implemented by AwContents (or ContentViewCore within 83 * it). This class also contains WebView-specific APIs that require the creation of other 84 * adapters (otherwise org.chromium.content would depend on the webview.chromium package) 85 * and a small set of no-op deprecated APIs. 86 */ 87class WebViewChromium implements WebViewProvider, 88 WebViewProvider.ScrollDelegate, WebViewProvider.ViewDelegate { 89 90 private class WebViewChromiumRunQueue { 91 public WebViewChromiumRunQueue() { 92 mQueue = new ConcurrentLinkedQueue<Runnable>(); 93 } 94 95 public void addTask(Runnable task) { 96 mQueue.add(task); 97 if (mFactory.hasStarted()) { 98 ThreadUtils.runOnUiThread(new Runnable() { 99 @Override 100 public void run() { 101 drainQueue(); 102 } 103 }); 104 } 105 } 106 107 public void drainQueue() { 108 if (mQueue == null || mQueue.isEmpty()) { 109 return; 110 } 111 112 Runnable task = mQueue.poll(); 113 while(task != null) { 114 task.run(); 115 task = mQueue.poll(); 116 } 117 } 118 119 private Queue<Runnable> mQueue; 120 } 121 122 private WebViewChromiumRunQueue mRunQueue; 123 124 private static final String TAG = WebViewChromium.class.getSimpleName(); 125 126 // The WebView that this WebViewChromium is the provider for. 127 WebView mWebView; 128 // Lets us access protected View-derived methods on the WebView instance we're backing. 129 WebView.PrivateAccess mWebViewPrivate; 130 // The client adapter class. 131 private WebViewContentsClientAdapter mContentsClientAdapter; 132 133 // Variables for functionality provided by this adapter --------------------------------------- 134 private ContentSettingsAdapter mWebSettings; 135 // The WebView wrapper for ContentViewCore and required browser compontents. 136 private AwContents mAwContents; 137 // Non-null if this webview is using the GL accelerated draw path. 138 private DrawGLFunctor mGLfunctor; 139 140 private final WebView.HitTestResult mHitTestResult; 141 142 private final int mAppTargetSdkVersion; 143 144 private WebViewChromiumFactoryProvider mFactory; 145 146 // This does not touch any global / non-threadsafe state, but note that 147 // init is ofter called right after and is NOT threadsafe. 148 public WebViewChromium(WebViewChromiumFactoryProvider factory, WebView webView, 149 WebView.PrivateAccess webViewPrivate) { 150 mWebView = webView; 151 mWebViewPrivate = webViewPrivate; 152 mHitTestResult = new WebView.HitTestResult(); 153 mAppTargetSdkVersion = mWebView.getContext().getApplicationInfo().targetSdkVersion; 154 mFactory = factory; 155 mRunQueue = new WebViewChromiumRunQueue(); 156 } 157 158 static void completeWindowCreation(WebView parent, WebView child) { 159 AwContents parentContents = ((WebViewChromium) parent.getWebViewProvider()).mAwContents; 160 AwContents childContents = 161 child == null ? null : ((WebViewChromium) child.getWebViewProvider()).mAwContents; 162 parentContents.supplyContentsForPopup(childContents); 163 } 164 165 private <T> T runBlockingFuture(FutureTask<T> task) { 166 if (!mFactory.hasStarted()) throw new RuntimeException("Must be started before we block!"); 167 if (ThreadUtils.runningOnUiThread()) { 168 throw new IllegalStateException("This method should only be called off the UI thread"); 169 } 170 mRunQueue.addTask(task); 171 try { 172 return task.get(4, TimeUnit.SECONDS); 173 } catch (java.util.concurrent.TimeoutException e) { 174 throw new RuntimeException("Probable deadlock detected due to WebView API being called " 175 + "on incorrect thread while the UI thread is blocked.", e); 176 } catch (Exception e) { 177 throw new RuntimeException(e); 178 } 179 } 180 181 // We have a 4 second timeout to try to detect deadlocks to detect and aid in debuggin 182 // deadlocks. 183 // Do not call this method while on the UI thread! 184 private void runVoidTaskOnUiThreadBlocking(Runnable r) { 185 FutureTask<Void> task = new FutureTask<Void>(r, null); 186 runBlockingFuture(task); 187 } 188 189 private <T> T runOnUiThreadBlocking(Callable<T> c) { 190 return runBlockingFuture(new FutureTask<T>(c)); 191 } 192 193 // WebViewProvider methods -------------------------------------------------------------------- 194 195 @Override 196 // BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree 197 // so is ignored. TODO: remove it from WebViewProvider. 198 public void init(final Map<String, Object> javaScriptInterfaces, 199 final boolean privateBrowsing) { 200 if (privateBrowsing) { 201 mFactory.startYourEngines(true); 202 final String msg = "Private browsing is not supported in WebView."; 203 if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) { 204 throw new IllegalArgumentException(msg); 205 } else { 206 Log.w(TAG, msg); 207 TextView warningLabel = new TextView(mWebView.getContext()); 208 warningLabel.setText(mWebView.getContext().getString( 209 com.android.internal.R.string.webviewchromium_private_browsing_warning)); 210 mWebView.addView(warningLabel); 211 } 212 } 213 214 // We will defer real initialization until we know which thread to do it on, unless: 215 // - we are on the main thread already (common case), 216 // - the app is targeting >= JB MR2, in which case checkThread enforces that all usage 217 // comes from a single thread. (Note in JB MR2 this exception was in WebView.java). 218 if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 219 mFactory.startYourEngines(false); 220 checkThread(); 221 } else if (!mFactory.hasStarted()) { 222 if (Looper.myLooper() == Looper.getMainLooper()) { 223 mFactory.startYourEngines(true); 224 } 225 } 226 227 final boolean isAccessFromFileURLsGrantedByDefault = 228 mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN; 229 final boolean areLegacyQuirksEnabled = 230 mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT; 231 232 mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView); 233 mWebSettings = new ContentSettingsAdapter(new AwSettings( 234 mWebView.getContext(), isAccessFromFileURLsGrantedByDefault, 235 areLegacyQuirksEnabled)); 236 237 if (mAppTargetSdkVersion <= Build.VERSION_CODES.KITKAT) { 238 mWebSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); 239 // On KK and older versions we always allowed third party cookies. 240 mWebSettings.setAcceptThirdPartyCookies(true); 241 } 242 243 mRunQueue.addTask(new Runnable() { 244 @Override 245 public void run() { 246 initForReal(); 247 if (privateBrowsing) { 248 // Intentionally irreversibly disable the webview instance, so that private 249 // user data cannot leak through misuse of a non-privateBrowing WebView 250 // instance. Can't just null out mAwContents as we never null-check it 251 // before use. 252 destroy(); 253 } 254 } 255 }); 256 } 257 258 private void initForReal() { 259 mAwContents = new AwContents(mFactory.getBrowserContext(), mWebView, mWebView.getContext(), 260 new InternalAccessAdapter(), new WebViewNativeGLDelegate(), 261 mContentsClientAdapter, mWebSettings.getAwSettings()); 262 263 if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) { 264 // On KK and above, favicons are automatically downloaded as the method 265 // old apps use to enable that behavior is deprecated. 266 AwContents.setShouldDownloadFavicons(); 267 } 268 269 if (mAppTargetSdkVersion <= Build.VERSION_CODES.KITKAT) { 270 // On KK and older versions, JavaScript objects injected via addJavascriptInterface 271 // were not inspectable. 272 mAwContents.disableJavascriptInterfacesInspection(); 273 } 274 275 // TODO: This assumes AwContents ignores second Paint param. 276 mAwContents.setLayerType(mWebView.getLayerType(), null); 277 } 278 279 void startYourEngine() { 280 mRunQueue.drainQueue(); 281 } 282 283 private RuntimeException createThreadException() { 284 return new IllegalStateException( 285 "Calling View methods on another thread than the UI thread."); 286 } 287 288 private boolean checkNeedsPost() { 289 boolean needsPost = !mFactory.hasStarted() || !ThreadUtils.runningOnUiThread(); 290 if (!needsPost && mAwContents == null) { 291 throw new IllegalStateException( 292 "AwContents must be created if we are not posting!"); 293 } 294 return needsPost; 295 } 296 297 // Intentionally not static, as no need to check thread on static methods 298 private void checkThread() { 299 if (!ThreadUtils.runningOnUiThread()) { 300 final RuntimeException threadViolation = createThreadException(); 301 ThreadUtils.postOnUiThread(new Runnable() { 302 @Override 303 public void run() { 304 throw threadViolation; 305 } 306 }); 307 throw createThreadException(); 308 } 309 } 310 311 @Override 312 public void setHorizontalScrollbarOverlay(final boolean overlay) { 313 if (checkNeedsPost()) { 314 mRunQueue.addTask(new Runnable() { 315 @Override 316 public void run() { 317 setHorizontalScrollbarOverlay(overlay); 318 } 319 }); 320 return; 321 } 322 mAwContents.setHorizontalScrollbarOverlay(overlay); 323 } 324 325 @Override 326 public void setVerticalScrollbarOverlay(final boolean overlay) { 327 if (checkNeedsPost()) { 328 mRunQueue.addTask(new Runnable() { 329 @Override 330 public void run() { 331 setVerticalScrollbarOverlay(overlay); 332 } 333 }); 334 return; 335 } 336 mAwContents.setVerticalScrollbarOverlay(overlay); 337 } 338 339 @Override 340 public boolean overlayHorizontalScrollbar() { 341 mFactory.startYourEngines(false); 342 if (checkNeedsPost()) { 343 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 344 @Override 345 public Boolean call() { 346 return overlayHorizontalScrollbar(); 347 } 348 }); 349 return ret; 350 } 351 return mAwContents.overlayHorizontalScrollbar(); 352 } 353 354 @Override 355 public boolean overlayVerticalScrollbar() { 356 mFactory.startYourEngines(false); 357 if (checkNeedsPost()) { 358 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 359 @Override 360 public Boolean call() { 361 return overlayVerticalScrollbar(); 362 } 363 }); 364 return ret; 365 } 366 return mAwContents.overlayVerticalScrollbar(); 367 } 368 369 @Override 370 public int getVisibleTitleHeight() { 371 // This is deprecated in WebView and should always return 0. 372 return 0; 373 } 374 375 @Override 376 public SslCertificate getCertificate() { 377 mFactory.startYourEngines(true); 378 if (checkNeedsPost()) { 379 SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() { 380 @Override 381 public SslCertificate call() { 382 return getCertificate(); 383 } 384 }); 385 return ret; 386 } 387 return mAwContents.getCertificate(); 388 } 389 390 @Override 391 public void setCertificate(SslCertificate certificate) { 392 // intentional no-op 393 } 394 395 @Override 396 public void savePassword(String host, String username, String password) { 397 // This is a deprecated API: intentional no-op. 398 } 399 400 @Override 401 public void setHttpAuthUsernamePassword(final String host, final String realm, 402 final String username, final String password) { 403 if (checkNeedsPost()) { 404 mRunQueue.addTask(new Runnable() { 405 @Override 406 public void run() { 407 setHttpAuthUsernamePassword(host, realm, username, password); 408 } 409 }); 410 return; 411 } 412 mAwContents.setHttpAuthUsernamePassword(host, realm, username, password); 413 } 414 415 @Override 416 public String[] getHttpAuthUsernamePassword(final String host, final String realm) { 417 mFactory.startYourEngines(true); 418 if (checkNeedsPost()) { 419 String[] ret = runOnUiThreadBlocking(new Callable<String[]>() { 420 @Override 421 public String[] call() { 422 return getHttpAuthUsernamePassword(host, realm); 423 } 424 }); 425 return ret; 426 } 427 return mAwContents.getHttpAuthUsernamePassword(host, realm); 428 } 429 430 @Override 431 public void destroy() { 432 if (checkNeedsPost()) { 433 mRunQueue.addTask(new Runnable() { 434 @Override 435 public void run() { 436 destroy(); 437 } 438 }); 439 return; 440 } 441 442 mAwContents.destroy(); 443 if (mGLfunctor != null) { 444 mGLfunctor.destroy(); 445 mGLfunctor = null; 446 } 447 } 448 449 @Override 450 public void setNetworkAvailable(final boolean networkUp) { 451 // Note that this purely toggles the JS navigator.online property. 452 // It does not in affect chromium or network stack state in any way. 453 if (checkNeedsPost()) { 454 mRunQueue.addTask(new Runnable() { 455 @Override 456 public void run() { 457 setNetworkAvailable(networkUp); 458 } 459 }); 460 return; 461 } 462 mAwContents.setNetworkAvailable(networkUp); 463 } 464 465 @Override 466 public WebBackForwardList saveState(final Bundle outState) { 467 mFactory.startYourEngines(true); 468 if (checkNeedsPost()) { 469 WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() { 470 @Override 471 public WebBackForwardList call() { 472 return saveState(outState); 473 } 474 }); 475 return ret; 476 } 477 if (outState == null) return null; 478 if (!mAwContents.saveState(outState)) return null; 479 return copyBackForwardList(); 480 } 481 482 @Override 483 public boolean savePicture(Bundle b, File dest) { 484 // Intentional no-op: hidden method on WebView. 485 return false; 486 } 487 488 @Override 489 public boolean restorePicture(Bundle b, File src) { 490 // Intentional no-op: hidden method on WebView. 491 return false; 492 } 493 494 @Override 495 public WebBackForwardList restoreState(final Bundle inState) { 496 mFactory.startYourEngines(true); 497 if (checkNeedsPost()) { 498 WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() { 499 @Override 500 public WebBackForwardList call() { 501 return restoreState(inState); 502 } 503 }); 504 return ret; 505 } 506 if (inState == null) return null; 507 if (!mAwContents.restoreState(inState)) return null; 508 return copyBackForwardList(); 509 } 510 511 @Override 512 public void loadUrl(final String url, Map<String, String> additionalHttpHeaders) { 513 // TODO: We may actually want to do some sanity checks here (like filter about://chrome). 514 515 // For backwards compatibility, apps targeting less than K will have JS URLs evaluated 516 // directly and any result of the evaluation will not replace the current page content. 517 // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will 518 // have the result of that URL replace the content of the current page. 519 final String JAVASCRIPT_SCHEME = "javascript:"; 520 if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT && 521 url != null && url.startsWith(JAVASCRIPT_SCHEME)) { 522 mFactory.startYourEngines(true); 523 if (checkNeedsPost()) { 524 mRunQueue.addTask(new Runnable() { 525 @Override 526 public void run() { 527 mAwContents.evaluateJavaScriptEvenIfNotYetNavigated( 528 url.substring(JAVASCRIPT_SCHEME.length())); 529 } 530 }); 531 } else { 532 mAwContents.evaluateJavaScriptEvenIfNotYetNavigated( 533 url.substring(JAVASCRIPT_SCHEME.length())); 534 } 535 return; 536 } 537 538 LoadUrlParams params = new LoadUrlParams(url); 539 if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders); 540 loadUrlOnUiThread(params); 541 } 542 543 @Override 544 public void loadUrl(String url) { 545 // Early out to match old WebView implementation 546 if (url == null) { 547 return; 548 } 549 loadUrl(url, null); 550 } 551 552 @Override 553 public void postUrl(String url, byte[] postData) { 554 LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData); 555 Map<String,String> headers = new HashMap<String,String>(); 556 headers.put("Content-Type", "application/x-www-form-urlencoded"); 557 params.setExtraHeaders(headers); 558 loadUrlOnUiThread(params); 559 } 560 561 private static String fixupMimeType(String mimeType) { 562 return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType; 563 } 564 565 private static String fixupData(String data) { 566 return TextUtils.isEmpty(data) ? "" : data; 567 } 568 569 private static String fixupBase(String url) { 570 return TextUtils.isEmpty(url) ? "about:blank" : url; 571 } 572 573 private static String fixupHistory(String url) { 574 return TextUtils.isEmpty(url) ? "about:blank" : url; 575 } 576 577 private static boolean isBase64Encoded(String encoding) { 578 return "base64".equals(encoding); 579 } 580 581 @Override 582 public void loadData(String data, String mimeType, String encoding) { 583 loadUrlOnUiThread(LoadUrlParams.createLoadDataParams( 584 fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding))); 585 } 586 587 @Override 588 public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, 589 String historyUrl) { 590 data = fixupData(data); 591 mimeType = fixupMimeType(mimeType); 592 LoadUrlParams loadUrlParams; 593 baseUrl = fixupBase(baseUrl); 594 historyUrl = fixupHistory(historyUrl); 595 596 if (baseUrl.startsWith("data:")) { 597 // For backwards compatibility with WebViewClassic, we use the value of |encoding| 598 // as the charset, as long as it's not "base64". 599 boolean isBase64 = isBase64Encoded(encoding); 600 loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl( 601 data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding); 602 } else { 603 // When loading data with a non-data: base URL, the classic WebView would effectively 604 // "dump" that string of data into the WebView without going through regular URL 605 // loading steps such as decoding URL-encoded entities. We achieve this same behavior by 606 // base64 encoding the data that is passed here and then loading that as a data: URL. 607 try { 608 loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl( 609 Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType, 610 true, baseUrl, historyUrl, "utf-8"); 611 } catch (java.io.UnsupportedEncodingException e) { 612 Log.wtf(TAG, "Unable to load data string " + data, e); 613 return; 614 } 615 } 616 loadUrlOnUiThread(loadUrlParams); 617 } 618 619 private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) { 620 // This is the last point that we can delay starting the Chromium backend up 621 // and if the app has not caused us to bind the Chromium UI thread to a background thread 622 // we now bind Chromium's notion of the UI thread to the app main thread. 623 mFactory.startYourEngines(true); 624 if (checkNeedsPost()) { 625 // Disallowed in WebView API for apps targetting a new SDK 626 assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2; 627 mRunQueue.addTask(new Runnable() { 628 @Override 629 public void run() { 630 mAwContents.loadUrl(loadUrlParams); 631 } 632 }); 633 return; 634 } 635 mAwContents.loadUrl(loadUrlParams); 636 } 637 638 public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) { 639 checkThread(); 640 mAwContents.evaluateJavaScript(script, resultCallback); 641 } 642 643 @Override 644 public void saveWebArchive(String filename) { 645 saveWebArchive(filename, false, null); 646 } 647 648 @Override 649 public void saveWebArchive(final String basename, final boolean autoname, 650 final ValueCallback<String> callback) { 651 if (checkNeedsPost()) { 652 mRunQueue.addTask(new Runnable() { 653 @Override 654 public void run() { 655 saveWebArchive(basename, autoname, callback); 656 } 657 }); 658 return; 659 } 660 mAwContents.saveWebArchive(basename, autoname, callback); 661 } 662 663 @Override 664 public void stopLoading() { 665 if (checkNeedsPost()) { 666 mRunQueue.addTask(new Runnable() { 667 @Override 668 public void run() { 669 stopLoading(); 670 } 671 }); 672 return; 673 } 674 675 mAwContents.stopLoading(); 676 } 677 678 @Override 679 public void reload() { 680 if (checkNeedsPost()) { 681 mRunQueue.addTask(new Runnable() { 682 @Override 683 public void run() { 684 reload(); 685 } 686 }); 687 return; 688 } 689 mAwContents.reload(); 690 } 691 692 @Override 693 public boolean canGoBack() { 694 mFactory.startYourEngines(true); 695 if (checkNeedsPost()) { 696 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 697 @Override 698 public Boolean call() { 699 return canGoBack(); 700 } 701 }); 702 return ret; 703 } 704 return mAwContents.canGoBack(); 705 } 706 707 @Override 708 public void goBack() { 709 if (checkNeedsPost()) { 710 mRunQueue.addTask(new Runnable() { 711 @Override 712 public void run() { 713 goBack(); 714 } 715 }); 716 return; 717 } 718 mAwContents.goBack(); 719 } 720 721 @Override 722 public boolean canGoForward() { 723 mFactory.startYourEngines(true); 724 if (checkNeedsPost()) { 725 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 726 @Override 727 public Boolean call() { 728 return canGoForward(); 729 } 730 }); 731 return ret; 732 } 733 return mAwContents.canGoForward(); 734 } 735 736 @Override 737 public void goForward() { 738 if (checkNeedsPost()) { 739 mRunQueue.addTask(new Runnable() { 740 @Override 741 public void run() { 742 goForward(); 743 } 744 }); 745 return; 746 } 747 mAwContents.goForward(); 748 } 749 750 @Override 751 public boolean canGoBackOrForward(final int steps) { 752 mFactory.startYourEngines(true); 753 if (checkNeedsPost()) { 754 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 755 @Override 756 public Boolean call() { 757 return canGoBackOrForward(steps); 758 } 759 }); 760 return ret; 761 } 762 return mAwContents.canGoBackOrForward(steps); 763 } 764 765 @Override 766 public void goBackOrForward(final int steps) { 767 if (checkNeedsPost()) { 768 mRunQueue.addTask(new Runnable() { 769 @Override 770 public void run() { 771 goBackOrForward(steps); 772 } 773 }); 774 return; 775 } 776 mAwContents.goBackOrForward(steps); 777 } 778 779 @Override 780 public boolean isPrivateBrowsingEnabled() { 781 // Not supported in this WebView implementation. 782 return false; 783 } 784 785 @Override 786 public boolean pageUp(final boolean top) { 787 mFactory.startYourEngines(true); 788 if (checkNeedsPost()) { 789 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 790 @Override 791 public Boolean call() { 792 return pageUp(top); 793 } 794 }); 795 return ret; 796 } 797 return mAwContents.pageUp(top); 798 } 799 800 @Override 801 public boolean pageDown(final boolean bottom) { 802 mFactory.startYourEngines(true); 803 if (checkNeedsPost()) { 804 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 805 @Override 806 public Boolean call() { 807 return pageDown(bottom); 808 } 809 }); 810 return ret; 811 } 812 return mAwContents.pageDown(bottom); 813 } 814 815 @Override 816 public void clearView() { 817 if (checkNeedsPost()) { 818 mRunQueue.addTask(new Runnable() { 819 @Override 820 public void run() { 821 clearView(); 822 } 823 }); 824 return; 825 } 826 mAwContents.clearView(); 827 } 828 829 @Override 830 public Picture capturePicture() { 831 mFactory.startYourEngines(true); 832 if (checkNeedsPost()) { 833 Picture ret = runOnUiThreadBlocking(new Callable<Picture>() { 834 @Override 835 public Picture call() { 836 return capturePicture(); 837 } 838 }); 839 return ret; 840 } 841 return mAwContents.capturePicture(); 842 } 843 844 @Override 845 public float getScale() { 846 // No checkThread() as it is mostly thread safe (workaround for b/10652991). 847 mFactory.startYourEngines(true); 848 return mAwContents.getScale(); 849 } 850 851 @Override 852 public void setInitialScale(final int scaleInPercent) { 853 // No checkThread() as it is thread safe 854 mWebSettings.getAwSettings().setInitialPageScale(scaleInPercent); 855 } 856 857 @Override 858 public void invokeZoomPicker() { 859 if (checkNeedsPost()) { 860 mRunQueue.addTask(new Runnable() { 861 @Override 862 public void run() { 863 invokeZoomPicker(); 864 } 865 }); 866 return; 867 } 868 mAwContents.invokeZoomPicker(); 869 } 870 871 @Override 872 public WebView.HitTestResult getHitTestResult() { 873 mFactory.startYourEngines(true); 874 if (checkNeedsPost()) { 875 WebView.HitTestResult ret = runOnUiThreadBlocking( 876 new Callable<WebView.HitTestResult>() { 877 @Override 878 public WebView.HitTestResult call() { 879 return getHitTestResult(); 880 } 881 }); 882 return ret; 883 } 884 AwContents.HitTestData data = mAwContents.getLastHitTestResult(); 885 mHitTestResult.setType(data.hitTestResultType); 886 mHitTestResult.setExtra(data.hitTestResultExtraData); 887 return mHitTestResult; 888 } 889 890 @Override 891 public void requestFocusNodeHref(final Message hrefMsg) { 892 if (checkNeedsPost()) { 893 mRunQueue.addTask(new Runnable() { 894 @Override 895 public void run() { 896 requestFocusNodeHref(hrefMsg); 897 } 898 }); 899 return; 900 } 901 mAwContents.requestFocusNodeHref(hrefMsg); 902 } 903 904 @Override 905 public void requestImageRef(final Message msg) { 906 if (checkNeedsPost()) { 907 mRunQueue.addTask(new Runnable() { 908 @Override 909 public void run() { 910 requestImageRef(msg); 911 } 912 }); 913 return; 914 } 915 mAwContents.requestImageRef(msg); 916 } 917 918 @Override 919 public String getUrl() { 920 mFactory.startYourEngines(true); 921 if (checkNeedsPost()) { 922 String ret = runOnUiThreadBlocking(new Callable<String>() { 923 @Override 924 public String call() { 925 return getUrl(); 926 } 927 }); 928 return ret; 929 } 930 String url = mAwContents.getUrl(); 931 if (url == null || url.trim().isEmpty()) return null; 932 return url; 933 } 934 935 @Override 936 public String getOriginalUrl() { 937 mFactory.startYourEngines(true); 938 if (checkNeedsPost()) { 939 String ret = runOnUiThreadBlocking(new Callable<String>() { 940 @Override 941 public String call() { 942 return getOriginalUrl(); 943 } 944 }); 945 return ret; 946 } 947 String url = mAwContents.getOriginalUrl(); 948 if (url == null || url.trim().isEmpty()) return null; 949 return url; 950 } 951 952 @Override 953 public String getTitle() { 954 mFactory.startYourEngines(true); 955 if (checkNeedsPost()) { 956 String ret = runOnUiThreadBlocking(new Callable<String>() { 957 @Override 958 public String call() { 959 return getTitle(); 960 } 961 }); 962 return ret; 963 } 964 return mAwContents.getTitle(); 965 } 966 967 @Override 968 public Bitmap getFavicon() { 969 mFactory.startYourEngines(true); 970 if (checkNeedsPost()) { 971 Bitmap ret = runOnUiThreadBlocking(new Callable<Bitmap>() { 972 @Override 973 public Bitmap call() { 974 return getFavicon(); 975 } 976 }); 977 return ret; 978 } 979 return mAwContents.getFavicon(); 980 } 981 982 @Override 983 public String getTouchIconUrl() { 984 // Intentional no-op: hidden method on WebView. 985 return null; 986 } 987 988 @Override 989 public int getProgress() { 990 if (mAwContents == null) return 100; 991 // No checkThread() because the value is cached java side (workaround for b/10533304). 992 return mAwContents.getMostRecentProgress(); 993 } 994 995 @Override 996 public int getContentHeight() { 997 if (mAwContents == null) return 0; 998 // No checkThread() as it is mostly thread safe (workaround for b/10594869). 999 return mAwContents.getContentHeightCss(); 1000 } 1001 1002 @Override 1003 public int getContentWidth() { 1004 if (mAwContents == null) return 0; 1005 // No checkThread() as it is mostly thread safe (workaround for b/10594869). 1006 return mAwContents.getContentWidthCss(); 1007 } 1008 1009 @Override 1010 public void pauseTimers() { 1011 if (checkNeedsPost()) { 1012 mRunQueue.addTask(new Runnable() { 1013 @Override 1014 public void run() { 1015 pauseTimers(); 1016 } 1017 }); 1018 return; 1019 } 1020 mAwContents.pauseTimers(); 1021 } 1022 1023 @Override 1024 public void resumeTimers() { 1025 if (checkNeedsPost()) { 1026 mRunQueue.addTask(new Runnable() { 1027 @Override 1028 public void run() { 1029 resumeTimers(); 1030 } 1031 }); 1032 return; 1033 } 1034 mAwContents.resumeTimers(); 1035 } 1036 1037 @Override 1038 public void onPause() { 1039 if (checkNeedsPost()) { 1040 mRunQueue.addTask(new Runnable() { 1041 @Override 1042 public void run() { 1043 onPause(); 1044 } 1045 }); 1046 return; 1047 } 1048 mAwContents.onPause(); 1049 } 1050 1051 @Override 1052 public void onResume() { 1053 if (checkNeedsPost()) { 1054 mRunQueue.addTask(new Runnable() { 1055 @Override 1056 public void run() { 1057 onResume(); 1058 } 1059 }); 1060 return; 1061 } 1062 mAwContents.onResume(); 1063 } 1064 1065 @Override 1066 public boolean isPaused() { 1067 mFactory.startYourEngines(true); 1068 if (checkNeedsPost()) { 1069 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1070 @Override 1071 public Boolean call() { 1072 return isPaused(); 1073 } 1074 }); 1075 return ret; 1076 } 1077 return mAwContents.isPaused(); 1078 } 1079 1080 @Override 1081 public void freeMemory() { 1082 // Intentional no-op. Memory is managed automatically by Chromium. 1083 } 1084 1085 @Override 1086 public void clearCache(final boolean includeDiskFiles) { 1087 if (checkNeedsPost()) { 1088 mRunQueue.addTask(new Runnable() { 1089 @Override 1090 public void run() { 1091 clearCache(includeDiskFiles); 1092 } 1093 }); 1094 return; 1095 } 1096 mAwContents.clearCache(includeDiskFiles); 1097 } 1098 1099 /** 1100 * This is a poorly named method, but we keep it for historical reasons. 1101 */ 1102 @Override 1103 public void clearFormData() { 1104 if (checkNeedsPost()) { 1105 mRunQueue.addTask(new Runnable() { 1106 @Override 1107 public void run() { 1108 clearFormData(); 1109 } 1110 }); 1111 return; 1112 } 1113 mAwContents.hideAutofillPopup(); 1114 } 1115 1116 @Override 1117 public void clearHistory() { 1118 if (checkNeedsPost()) { 1119 mRunQueue.addTask(new Runnable() { 1120 @Override 1121 public void run() { 1122 clearHistory(); 1123 } 1124 }); 1125 return; 1126 } 1127 mAwContents.clearHistory(); 1128 } 1129 1130 @Override 1131 public void clearSslPreferences() { 1132 if (checkNeedsPost()) { 1133 mRunQueue.addTask(new Runnable() { 1134 @Override 1135 public void run() { 1136 clearSslPreferences(); 1137 } 1138 }); 1139 return; 1140 } 1141 mAwContents.clearSslPreferences(); 1142 } 1143 1144 @Override 1145 public WebBackForwardList copyBackForwardList() { 1146 mFactory.startYourEngines(true); 1147 if (checkNeedsPost()) { 1148 WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() { 1149 @Override 1150 public WebBackForwardList call() { 1151 return copyBackForwardList(); 1152 } 1153 }); 1154 return ret; 1155 } 1156 return new WebBackForwardListChromium( 1157 mAwContents.getNavigationHistory()); 1158 } 1159 1160 @Override 1161 public void setFindListener(WebView.FindListener listener) { 1162 mContentsClientAdapter.setFindListener(listener); 1163 } 1164 1165 @Override 1166 public void findNext(final boolean forwards) { 1167 if (checkNeedsPost()) { 1168 mRunQueue.addTask(new Runnable() { 1169 @Override 1170 public void run() { 1171 findNext(forwards); 1172 } 1173 }); 1174 return; 1175 } 1176 mAwContents.findNext(forwards); 1177 } 1178 1179 @Override 1180 public int findAll(final String searchString) { 1181 findAllAsync(searchString); 1182 return 0; 1183 } 1184 1185 @Override 1186 public void findAllAsync(final String searchString) { 1187 if (checkNeedsPost()) { 1188 mRunQueue.addTask(new Runnable() { 1189 @Override 1190 public void run() { 1191 findAllAsync(searchString); 1192 } 1193 }); 1194 return; 1195 } 1196 mAwContents.findAllAsync(searchString); 1197 } 1198 1199 @Override 1200 public boolean showFindDialog(final String text, final boolean showIme) { 1201 mFactory.startYourEngines(false); 1202 if (checkNeedsPost()) { 1203 return false; 1204 } 1205 if (mWebView.getParent() == null) { 1206 return false; 1207 } 1208 1209 FindActionModeCallback findAction = new FindActionModeCallback(mWebView.getContext()); 1210 if (findAction == null) { 1211 return false; 1212 } 1213 1214 mWebView.startActionMode(findAction); 1215 findAction.setWebView(mWebView); 1216 if (showIme) { 1217 findAction.showSoftInput(); 1218 } 1219 1220 if (text != null) { 1221 findAction.setText(text); 1222 findAction.findAll(); 1223 } 1224 1225 return true; 1226 } 1227 1228 @Override 1229 public void notifyFindDialogDismissed() { 1230 if (checkNeedsPost()) { 1231 mRunQueue.addTask(new Runnable() { 1232 @Override 1233 public void run() { 1234 notifyFindDialogDismissed(); 1235 } 1236 }); 1237 return; 1238 } 1239 clearMatches(); 1240 } 1241 1242 @Override 1243 public void clearMatches() { 1244 if (checkNeedsPost()) { 1245 mRunQueue.addTask(new Runnable() { 1246 @Override 1247 public void run() { 1248 clearMatches(); 1249 } 1250 }); 1251 return; 1252 } 1253 mAwContents.clearMatches(); 1254 } 1255 1256 @Override 1257 public void documentHasImages(final Message response) { 1258 if (checkNeedsPost()) { 1259 mRunQueue.addTask(new Runnable() { 1260 @Override 1261 public void run() { 1262 documentHasImages(response); 1263 } 1264 }); 1265 return; 1266 } 1267 mAwContents.documentHasImages(response); 1268 } 1269 1270 @Override 1271 public void setWebViewClient(WebViewClient client) { 1272 mContentsClientAdapter.setWebViewClient(client); 1273 } 1274 1275 @Override 1276 public void setDownloadListener(DownloadListener listener) { 1277 mContentsClientAdapter.setDownloadListener(listener); 1278 } 1279 1280 @Override 1281 public void setWebChromeClient(WebChromeClient client) { 1282 mContentsClientAdapter.setWebChromeClient(client); 1283 } 1284 1285 @Override 1286 public void setPictureListener(final WebView.PictureListener listener) { 1287 if (checkNeedsPost()) { 1288 mRunQueue.addTask(new Runnable() { 1289 @Override 1290 public void run() { 1291 setPictureListener(listener); 1292 } 1293 }); 1294 return; 1295 } 1296 mContentsClientAdapter.setPictureListener(listener); 1297 mAwContents.enableOnNewPicture(listener != null, 1298 mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2); 1299 } 1300 1301 @Override 1302 public void addJavascriptInterface(final Object obj, final String interfaceName) { 1303 if (checkNeedsPost()) { 1304 mRunQueue.addTask(new Runnable() { 1305 @Override 1306 public void run() { 1307 addJavascriptInterface(obj, interfaceName); 1308 } 1309 }); 1310 return; 1311 } 1312 Class<? extends Annotation> requiredAnnotation = null; 1313 if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 1314 requiredAnnotation = JavascriptInterface.class; 1315 } 1316 mAwContents.addPossiblyUnsafeJavascriptInterface(obj, interfaceName, requiredAnnotation); 1317 } 1318 1319 @Override 1320 public void removeJavascriptInterface(final String interfaceName) { 1321 if (checkNeedsPost()) { 1322 mRunQueue.addTask(new Runnable() { 1323 @Override 1324 public void run() { 1325 removeJavascriptInterface(interfaceName); 1326 } 1327 }); 1328 return; 1329 } 1330 mAwContents.removeJavascriptInterface(interfaceName); 1331 } 1332 1333 @Override 1334 public WebSettings getSettings() { 1335 return mWebSettings; 1336 } 1337 1338 @Override 1339 public void setMapTrackballToArrowKeys(boolean setMap) { 1340 // This is a deprecated API: intentional no-op. 1341 } 1342 1343 @Override 1344 public void flingScroll(final int vx, final int vy) { 1345 if (checkNeedsPost()) { 1346 mRunQueue.addTask(new Runnable() { 1347 @Override 1348 public void run() { 1349 flingScroll(vx, vy); 1350 } 1351 }); 1352 return; 1353 } 1354 mAwContents.flingScroll(vx, vy); 1355 } 1356 1357 @Override 1358 public View getZoomControls() { 1359 mFactory.startYourEngines(false); 1360 if (checkNeedsPost()) { 1361 return null; 1362 } 1363 1364 // This was deprecated in 2009 and hidden in JB MR1, so just provide the minimum needed 1365 // to stop very out-dated applications from crashing. 1366 Log.w(TAG, "WebView doesn't support getZoomControls"); 1367 return mAwContents.getSettings().supportZoom() ? new View(mWebView.getContext()) : null; 1368 } 1369 1370 @Override 1371 public boolean canZoomIn() { 1372 if (checkNeedsPost()) { 1373 return false; 1374 } 1375 return mAwContents.canZoomIn(); 1376 } 1377 1378 @Override 1379 public boolean canZoomOut() { 1380 if (checkNeedsPost()) { 1381 return false; 1382 } 1383 return mAwContents.canZoomOut(); 1384 } 1385 1386 @Override 1387 public boolean zoomIn() { 1388 mFactory.startYourEngines(true); 1389 if (checkNeedsPost()) { 1390 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1391 @Override 1392 public Boolean call() { 1393 return zoomIn(); 1394 } 1395 }); 1396 return ret; 1397 } 1398 return mAwContents.zoomIn(); 1399 } 1400 1401 @Override 1402 public boolean zoomOut() { 1403 mFactory.startYourEngines(true); 1404 if (checkNeedsPost()) { 1405 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1406 @Override 1407 public Boolean call() { 1408 return zoomOut(); 1409 } 1410 }); 1411 return ret; 1412 } 1413 return mAwContents.zoomOut(); 1414 } 1415 1416 //TODO: remove once frameworks/base is updated 1417 //@Override 1418 public boolean zoomBy(float factor) { 1419 mFactory.startYourEngines(true); 1420 // This is an L API and therefore we can enforce stricter threading constraints. 1421 checkThread(); 1422 //TODO: remove once Chromium-side CL is merged. 1423 //return mAwContents.zoomBy(factor); 1424 return false; 1425 } 1426 1427 @Override 1428 public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) { 1429 // Intentional no-op 1430 } 1431 1432 @Override 1433 public View findHierarchyView(String className, int hashCode) { 1434 // Intentional no-op 1435 return null; 1436 } 1437 1438 // WebViewProvider glue methods --------------------------------------------------------------- 1439 1440 @Override 1441 // This needs to be kept thread safe! 1442 public WebViewProvider.ViewDelegate getViewDelegate() { 1443 return this; 1444 } 1445 1446 @Override 1447 // This needs to be kept thread safe! 1448 public WebViewProvider.ScrollDelegate getScrollDelegate() { 1449 return this; 1450 } 1451 1452 1453 // WebViewProvider.ViewDelegate implementation ------------------------------------------------ 1454 1455 // TODO: remove from WebViewProvider and use default implementation from 1456 // ViewGroup. 1457 // @Override 1458 public boolean shouldDelayChildPressedState() { 1459 mFactory.startYourEngines(false); 1460 if (checkNeedsPost()) { 1461 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1462 @Override 1463 public Boolean call() { 1464 return shouldDelayChildPressedState(); 1465 } 1466 }); 1467 return ret; 1468 } 1469 return true; 1470 } 1471 1472// @Override 1473 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 1474 mFactory.startYourEngines(false); 1475 if (checkNeedsPost()) { 1476 AccessibilityNodeProvider ret = runOnUiThreadBlocking( 1477 new Callable<AccessibilityNodeProvider>() { 1478 @Override 1479 public AccessibilityNodeProvider call() { 1480 return getAccessibilityNodeProvider(); 1481 } 1482 }); 1483 return ret; 1484 } 1485 return mAwContents.getAccessibilityNodeProvider(); 1486 } 1487 1488 @Override 1489 public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) { 1490 mFactory.startYourEngines(false); 1491 if (checkNeedsPost()) { 1492 runVoidTaskOnUiThreadBlocking(new Runnable() { 1493 @Override 1494 public void run() { 1495 onInitializeAccessibilityNodeInfo(info); 1496 } 1497 }); 1498 return; 1499 } 1500 mAwContents.onInitializeAccessibilityNodeInfo(info); 1501 } 1502 1503 @Override 1504 public void onInitializeAccessibilityEvent(final AccessibilityEvent event) { 1505 mFactory.startYourEngines(false); 1506 if (checkNeedsPost()) { 1507 runVoidTaskOnUiThreadBlocking(new Runnable() { 1508 @Override 1509 public void run() { 1510 onInitializeAccessibilityEvent(event); 1511 } 1512 }); 1513 return; 1514 } 1515 mAwContents.onInitializeAccessibilityEvent(event); 1516 } 1517 1518 @Override 1519 public boolean performAccessibilityAction(final int action, final Bundle arguments) { 1520 mFactory.startYourEngines(false); 1521 if (checkNeedsPost()) { 1522 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1523 @Override 1524 public Boolean call() { 1525 return performAccessibilityAction(action, arguments); 1526 } 1527 }); 1528 return ret; 1529 } 1530 if (mAwContents.supportsAccessibilityAction(action)) { 1531 return mAwContents.performAccessibilityAction(action, arguments); 1532 } 1533 return mWebViewPrivate.super_performAccessibilityAction(action, arguments); 1534 } 1535 1536 @Override 1537 public void setOverScrollMode(final int mode) { 1538 // This gets called from the android.view.View c'tor that WebView inherits from. This 1539 // causes the method to be called when mAwContents == null. 1540 // It's safe to ignore these calls however since AwContents will read the current value of 1541 // this setting when it's created. 1542 if (mAwContents == null) return; 1543 1544 if (checkNeedsPost()) { 1545 mRunQueue.addTask(new Runnable() { 1546 @Override 1547 public void run() { 1548 setOverScrollMode(mode); 1549 } 1550 }); 1551 return; 1552 } 1553 mAwContents.setOverScrollMode(mode); 1554 } 1555 1556 @Override 1557 public void setScrollBarStyle(final int style) { 1558 if (checkNeedsPost()) { 1559 mRunQueue.addTask(new Runnable() { 1560 @Override 1561 public void run() { 1562 setScrollBarStyle(style); 1563 } 1564 }); 1565 return; 1566 } 1567 mAwContents.setScrollBarStyle(style); 1568 } 1569 1570 @Override 1571 public void onDrawVerticalScrollBar(final Canvas canvas, final Drawable scrollBar, final int l, 1572 final int t, final int r, final int b) { 1573 // WebViewClassic was overriding this method to handle rubberband over-scroll. Since 1574 // WebViewChromium doesn't support that the vanilla implementation of this method can be 1575 // used. 1576 mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b); 1577 } 1578 1579 @Override 1580 public void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX, 1581 final boolean clampedY) { 1582 if (checkNeedsPost()) { 1583 mRunQueue.addTask(new Runnable() { 1584 @Override 1585 public void run() { 1586 onOverScrolled(scrollX, scrollY, clampedX, clampedY); 1587 } 1588 }); 1589 return; 1590 } 1591 mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); 1592 } 1593 1594 @Override 1595 public void onWindowVisibilityChanged(final int visibility) { 1596 if (checkNeedsPost()) { 1597 mRunQueue.addTask(new Runnable() { 1598 @Override 1599 public void run() { 1600 onWindowVisibilityChanged(visibility); 1601 } 1602 }); 1603 return; 1604 } 1605 mAwContents.onWindowVisibilityChanged(visibility); 1606 } 1607 1608 @Override 1609 public void onDraw(final Canvas canvas) { 1610 mFactory.startYourEngines(true); 1611 if (checkNeedsPost()) { 1612 runVoidTaskOnUiThreadBlocking(new Runnable() { 1613 @Override 1614 public void run() { 1615 onDraw(canvas); 1616 } 1617 }); 1618 return; 1619 } 1620 mAwContents.onDraw(canvas); 1621 } 1622 1623 @Override 1624 public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) { 1625 // This API is our strongest signal from the View system that this 1626 // WebView is going to be bound to a View hierarchy and so at this 1627 // point we must bind Chromium's UI thread to the current thread. 1628 mFactory.startYourEngines(false); 1629 checkThread(); 1630 mWebViewPrivate.super_setLayoutParams(layoutParams); 1631 } 1632 1633 @Override 1634 public boolean performLongClick() { 1635 // Return false unless the WebView is attached to a View with a parent 1636 return mWebView.getParent() != null ? mWebViewPrivate.super_performLongClick() : false; 1637 } 1638 1639 @Override 1640 public void onConfigurationChanged(final Configuration newConfig) { 1641 if (checkNeedsPost()) { 1642 mRunQueue.addTask(new Runnable() { 1643 @Override 1644 public void run() { 1645 onConfigurationChanged(newConfig); 1646 } 1647 }); 1648 return; 1649 } 1650 mAwContents.onConfigurationChanged(newConfig); 1651 } 1652 1653 @Override 1654 public InputConnection onCreateInputConnection(final EditorInfo outAttrs) { 1655 mFactory.startYourEngines(false); 1656 if (checkNeedsPost()) { 1657 return null; 1658 } 1659 return mAwContents.onCreateInputConnection(outAttrs); 1660 } 1661 1662 @Override 1663 public boolean onKeyMultiple(final int keyCode, final int repeatCount, final KeyEvent event) { 1664 mFactory.startYourEngines(false); 1665 if (checkNeedsPost()) { 1666 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1667 @Override 1668 public Boolean call() { 1669 return onKeyMultiple(keyCode, repeatCount, event); 1670 } 1671 }); 1672 return ret; 1673 } 1674 UnimplementedWebViewApi.invoke(); 1675 return false; 1676 } 1677 1678 @Override 1679 public boolean onKeyDown(final int keyCode, final KeyEvent event) { 1680 mFactory.startYourEngines(false); 1681 if (checkNeedsPost()) { 1682 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1683 @Override 1684 public Boolean call() { 1685 return onKeyDown(keyCode, event); 1686 } 1687 }); 1688 return ret; 1689 } 1690 UnimplementedWebViewApi.invoke(); 1691 return false; 1692 } 1693 1694 @Override 1695 public boolean onKeyUp(final int keyCode, final KeyEvent event) { 1696 mFactory.startYourEngines(false); 1697 if (checkNeedsPost()) { 1698 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1699 @Override 1700 public Boolean call() { 1701 return onKeyUp(keyCode, event); 1702 } 1703 }); 1704 return ret; 1705 } 1706 return mAwContents.onKeyUp(keyCode, event); 1707 } 1708 1709 @Override 1710 public void onAttachedToWindow() { 1711 // This API is our strongest signal from the View system that this 1712 // WebView is going to be bound to a View hierarchy and so at this 1713 // point we must bind Chromium's UI thread to the current thread. 1714 mFactory.startYourEngines(false); 1715 checkThread(); 1716 mAwContents.onAttachedToWindow(); 1717 } 1718 1719 @Override 1720 public void onDetachedFromWindow() { 1721 if (checkNeedsPost()) { 1722 mRunQueue.addTask(new Runnable() { 1723 @Override 1724 public void run() { 1725 onDetachedFromWindow(); 1726 } 1727 }); 1728 return; 1729 } 1730 1731 mAwContents.onDetachedFromWindow(); 1732 } 1733 1734 @Override 1735 public void onVisibilityChanged(final View changedView, final int visibility) { 1736 // The AwContents will find out the container view visibility before the first draw so we 1737 // can safely ignore onVisibilityChanged callbacks that happen before init(). 1738 if (mAwContents == null) return; 1739 1740 if (checkNeedsPost()) { 1741 mRunQueue.addTask(new Runnable() { 1742 @Override 1743 public void run() { 1744 onVisibilityChanged(changedView, visibility); 1745 } 1746 }); 1747 return; 1748 } 1749 mAwContents.onVisibilityChanged(changedView, visibility); 1750 } 1751 1752 @Override 1753 public void onWindowFocusChanged(final boolean hasWindowFocus) { 1754 if (checkNeedsPost()) { 1755 mRunQueue.addTask(new Runnable() { 1756 @Override 1757 public void run() { 1758 onWindowFocusChanged(hasWindowFocus); 1759 } 1760 }); 1761 return; 1762 } 1763 mAwContents.onWindowFocusChanged(hasWindowFocus); 1764 } 1765 1766 @Override 1767 public void onFocusChanged(final boolean focused, final int direction, 1768 final Rect previouslyFocusedRect) { 1769 if (checkNeedsPost()) { 1770 mRunQueue.addTask(new Runnable() { 1771 @Override 1772 public void run() { 1773 onFocusChanged(focused, direction, previouslyFocusedRect); 1774 } 1775 }); 1776 return; 1777 } 1778 mAwContents.onFocusChanged(focused, direction, previouslyFocusedRect); 1779 } 1780 1781 @Override 1782 public boolean setFrame(final int left, final int top, final int right, final int bottom) { 1783 return mWebViewPrivate.super_setFrame(left, top, right, bottom); 1784 } 1785 1786 @Override 1787 public void onSizeChanged(final int w, final int h, final int ow, final int oh) { 1788 if (checkNeedsPost()) { 1789 mRunQueue.addTask(new Runnable() { 1790 @Override 1791 public void run() { 1792 onSizeChanged(w, h, ow, oh); 1793 } 1794 }); 1795 return; 1796 } 1797 mAwContents.onSizeChanged(w, h, ow, oh); 1798 } 1799 1800 @Override 1801 public void onScrollChanged(int l, int t, int oldl, int oldt) { 1802 } 1803 1804 @Override 1805 public boolean dispatchKeyEvent(final KeyEvent event) { 1806 mFactory.startYourEngines(false); 1807 if (checkNeedsPost()) { 1808 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1809 @Override 1810 public Boolean call() { 1811 return dispatchKeyEvent(event); 1812 } 1813 }); 1814 return ret; 1815 } 1816 return mAwContents.dispatchKeyEvent(event); 1817 } 1818 1819 @Override 1820 public boolean onTouchEvent(final MotionEvent ev) { 1821 mFactory.startYourEngines(false); 1822 if (checkNeedsPost()) { 1823 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1824 @Override 1825 public Boolean call() { 1826 return onTouchEvent(ev); 1827 } 1828 }); 1829 return ret; 1830 } 1831 return mAwContents.onTouchEvent(ev); 1832 } 1833 1834 @Override 1835 public boolean onHoverEvent(final MotionEvent event) { 1836 mFactory.startYourEngines(false); 1837 if (checkNeedsPost()) { 1838 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1839 @Override 1840 public Boolean call() { 1841 return onHoverEvent(event); 1842 } 1843 }); 1844 return ret; 1845 } 1846 return mAwContents.onHoverEvent(event); 1847 } 1848 1849 @Override 1850 public boolean onGenericMotionEvent(final MotionEvent event) { 1851 mFactory.startYourEngines(false); 1852 if (checkNeedsPost()) { 1853 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1854 @Override 1855 public Boolean call() { 1856 return onGenericMotionEvent(event); 1857 } 1858 }); 1859 return ret; 1860 } 1861 return mAwContents.onGenericMotionEvent(event); 1862 } 1863 1864 @Override 1865 public boolean onTrackballEvent(MotionEvent ev) { 1866 // Trackball event not handled, which eventually gets converted to DPAD keyevents 1867 return false; 1868 } 1869 1870 @Override 1871 public boolean requestFocus(final int direction, final Rect previouslyFocusedRect) { 1872 mFactory.startYourEngines(false); 1873 if (checkNeedsPost()) { 1874 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1875 @Override 1876 public Boolean call() { 1877 return requestFocus(direction, previouslyFocusedRect); 1878 } 1879 }); 1880 return ret; 1881 } 1882 mAwContents.requestFocus(); 1883 return mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect); 1884 } 1885 1886 @Override 1887 public void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 1888 mFactory.startYourEngines(false); 1889 if (checkNeedsPost()) { 1890 runVoidTaskOnUiThreadBlocking(new Runnable() { 1891 @Override 1892 public void run() { 1893 onMeasure(widthMeasureSpec, heightMeasureSpec); 1894 } 1895 }); 1896 return; 1897 } 1898 mAwContents.onMeasure(widthMeasureSpec, heightMeasureSpec); 1899 } 1900 1901 @Override 1902 public boolean requestChildRectangleOnScreen(final View child, final Rect rect, 1903 final boolean immediate) { 1904 mFactory.startYourEngines(false); 1905 if (checkNeedsPost()) { 1906 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1907 @Override 1908 public Boolean call() { 1909 return requestChildRectangleOnScreen(child, rect, immediate); 1910 } 1911 }); 1912 return ret; 1913 } 1914 return mAwContents.requestChildRectangleOnScreen(child, rect, immediate); 1915 } 1916 1917 @Override 1918 public void setBackgroundColor(final int color) { 1919 mFactory.startYourEngines(false); 1920 if (checkNeedsPost()) { 1921 ThreadUtils.postOnUiThread(new Runnable() { 1922 @Override 1923 public void run() { 1924 setBackgroundColor(color); 1925 } 1926 }); 1927 return; 1928 } 1929 mAwContents.setBackgroundColor(color); 1930 } 1931 1932 @Override 1933 public void setLayerType(final int layerType, final Paint paint) { 1934 // This can be called from WebView constructor in which case mAwContents 1935 // is still null. We set the layer type in initForReal in that case. 1936 if (mAwContents == null) return; 1937 if (checkNeedsPost()) { 1938 ThreadUtils.postOnUiThread(new Runnable() { 1939 @Override 1940 public void run() { 1941 setLayerType(layerType, paint); 1942 } 1943 }); 1944 return; 1945 } 1946 mAwContents.setLayerType(layerType, paint); 1947 } 1948 1949 // Remove from superclass 1950 public void preDispatchDraw(Canvas canvas) { 1951 // TODO(leandrogracia): remove this method from WebViewProvider if we think 1952 // we won't need it again. 1953 } 1954 1955 public void onStartTemporaryDetach() { 1956 mAwContents.onStartTemporaryDetach(); 1957 } 1958 1959 public void onFinishTemporaryDetach() { 1960 mAwContents.onFinishTemporaryDetach(); 1961 } 1962 1963 // WebViewProvider.ScrollDelegate implementation ---------------------------------------------- 1964 1965 @Override 1966 public int computeHorizontalScrollRange() { 1967 mFactory.startYourEngines(false); 1968 if (checkNeedsPost()) { 1969 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 1970 @Override 1971 public Integer call() { 1972 return computeHorizontalScrollRange(); 1973 } 1974 }); 1975 return ret; 1976 } 1977 return mAwContents.computeHorizontalScrollRange(); 1978 } 1979 1980 @Override 1981 public int computeHorizontalScrollOffset() { 1982 mFactory.startYourEngines(false); 1983 if (checkNeedsPost()) { 1984 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 1985 @Override 1986 public Integer call() { 1987 return computeHorizontalScrollOffset(); 1988 } 1989 }); 1990 return ret; 1991 } 1992 return mAwContents.computeHorizontalScrollOffset(); 1993 } 1994 1995 @Override 1996 public int computeVerticalScrollRange() { 1997 mFactory.startYourEngines(false); 1998 if (checkNeedsPost()) { 1999 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 2000 @Override 2001 public Integer call() { 2002 return computeVerticalScrollRange(); 2003 } 2004 }); 2005 return ret; 2006 } 2007 return mAwContents.computeVerticalScrollRange(); 2008 } 2009 2010 @Override 2011 public int computeVerticalScrollOffset() { 2012 mFactory.startYourEngines(false); 2013 if (checkNeedsPost()) { 2014 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 2015 @Override 2016 public Integer call() { 2017 return computeVerticalScrollOffset(); 2018 } 2019 }); 2020 return ret; 2021 } 2022 return mAwContents.computeVerticalScrollOffset(); 2023 } 2024 2025 @Override 2026 public int computeVerticalScrollExtent() { 2027 mFactory.startYourEngines(false); 2028 if (checkNeedsPost()) { 2029 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 2030 @Override 2031 public Integer call() { 2032 return computeVerticalScrollExtent(); 2033 } 2034 }); 2035 return ret; 2036 } 2037 return mAwContents.computeVerticalScrollExtent(); 2038 } 2039 2040 @Override 2041 public void computeScroll() { 2042 mFactory.startYourEngines(false); 2043 if (checkNeedsPost()) { 2044 runVoidTaskOnUiThreadBlocking(new Runnable() { 2045 @Override 2046 public void run() { 2047 computeScroll(); 2048 } 2049 }); 2050 return; 2051 } 2052 mAwContents.computeScroll(); 2053 } 2054 2055 @Override 2056 public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) { 2057 checkThread(); 2058 return new AwPrintDocumentAdapter(mAwContents.getPdfExporter(), documentName); 2059 } 2060 2061 // AwContents.NativeGLDelegate implementation -------------------------------------- 2062 private class WebViewNativeGLDelegate implements AwContents.NativeGLDelegate { 2063 @Override 2064 public boolean requestDrawGL(Canvas canvas, boolean waitForCompletion, 2065 View containerView) { 2066 if (mGLfunctor == null) { 2067 mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext()); 2068 } 2069 return mGLfunctor.requestDrawGL( 2070 (HardwareCanvas) canvas, containerView.getViewRootImpl(), waitForCompletion); 2071 } 2072 2073 @Override 2074 public void detachGLFunctor() { 2075 if (mGLfunctor != null) { 2076 mGLfunctor.detach(); 2077 } 2078 } 2079 } 2080 2081 // AwContents.InternalAccessDelegate implementation -------------------------------------- 2082 private class InternalAccessAdapter implements AwContents.InternalAccessDelegate { 2083 @Override 2084 public boolean drawChild(Canvas arg0, View arg1, long arg2) { 2085 UnimplementedWebViewApi.invoke(); 2086 return false; 2087 } 2088 2089 @Override 2090 public boolean super_onKeyUp(int arg0, KeyEvent arg1) { 2091 // Intentional no-op 2092 return false; 2093 } 2094 2095 @Override 2096 public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) { 2097 UnimplementedWebViewApi.invoke(); 2098 return false; 2099 } 2100 2101 @Override 2102 public boolean super_dispatchKeyEvent(KeyEvent event) { 2103 return mWebViewPrivate.super_dispatchKeyEvent(event); 2104 } 2105 2106 @Override 2107 public boolean super_onGenericMotionEvent(MotionEvent arg0) { 2108 return mWebViewPrivate.super_onGenericMotionEvent(arg0); 2109 } 2110 2111 @Override 2112 public void super_onConfigurationChanged(Configuration arg0) { 2113 // Intentional no-op 2114 } 2115 2116 @Override 2117 public int super_getScrollBarStyle() { 2118 return mWebViewPrivate.super_getScrollBarStyle(); 2119 } 2120 2121 @Override 2122 public boolean awakenScrollBars() { 2123 mWebViewPrivate.awakenScrollBars(0); 2124 // TODO: modify the WebView.PrivateAccess to provide a return value. 2125 return true; 2126 } 2127 2128 @Override 2129 public boolean super_awakenScrollBars(int arg0, boolean arg1) { 2130 // TODO: need method on WebView.PrivateAccess? 2131 UnimplementedWebViewApi.invoke(); 2132 return false; 2133 } 2134 2135 @Override 2136 public void onScrollChanged(int l, int t, int oldl, int oldt) { 2137 // Intentional no-op. 2138 // Chromium calls this directly to trigger accessibility events. That isn't needed 2139 // for WebView since super_scrollTo invokes onScrollChanged for us. 2140 } 2141 2142 @Override 2143 public void overScrollBy(int deltaX, int deltaY, 2144 int scrollX, int scrollY, 2145 int scrollRangeX, int scrollRangeY, 2146 int maxOverScrollX, int maxOverScrollY, 2147 boolean isTouchEvent) { 2148 mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY, 2149 scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 2150 } 2151 2152 @Override 2153 public void super_scrollTo(int scrollX, int scrollY) { 2154 mWebViewPrivate.super_scrollTo(scrollX, scrollY); 2155 } 2156 2157 @Override 2158 public void setMeasuredDimension(int measuredWidth, int measuredHeight) { 2159 mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight); 2160 } 2161 2162 // @Override 2163 public boolean super_onHoverEvent(MotionEvent event) { 2164 return mWebViewPrivate.super_onHoverEvent(event); 2165 } 2166 } 2167} 2168