WebViewChromium.java revision 06b29e22754df5cc9c951dc94f60541b4e1a6221
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 mRunQueue.addTask(new Runnable() { 238 @Override 239 public void run() { 240 initForReal(); 241 if (privateBrowsing) { 242 // Intentionally irreversibly disable the webview instance, so that private 243 // user data cannot leak through misuse of a non-privateBrowing WebView 244 // instance. Can't just null out mAwContents as we never null-check it 245 // before use. 246 destroy(); 247 } 248 } 249 }); 250 } 251 252 private void initForReal() { 253 mAwContents = new AwContents(mFactory.getBrowserContext(), mWebView, 254 new InternalAccessAdapter(), mContentsClientAdapter, mWebSettings.getAwSettings()); 255 256 if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) { 257 // On KK and above, favicons are automatically downloaded as the method 258 // old apps use to enable that behavior is deprecated. 259 AwContents.setShouldDownloadFavicons(); 260 } 261 262 if (mAppTargetSdkVersion <= Build.VERSION_CODES.KITKAT) { 263 // On KK and older versions, JavaScript objects injected via addJavascriptInterface 264 // were not inspectable. 265 mAwContents.disableJavascriptInterfacesInspection(); 266 } 267 268 // TODO: This assumes AwContents ignores second Paint param. 269 mAwContents.setLayerType(mWebView.getLayerType(), null); 270 } 271 272 void startYourEngine() { 273 mRunQueue.drainQueue(); 274 } 275 276 private RuntimeException createThreadException() { 277 return new IllegalStateException( 278 "Calling View methods on another thread than the UI thread."); 279 } 280 281 private boolean checkNeedsPost() { 282 boolean needsPost = !mFactory.hasStarted() || !ThreadUtils.runningOnUiThread(); 283 if (!needsPost && mAwContents == null) { 284 throw new IllegalStateException( 285 "AwContents must be created if we are not posting!"); 286 } 287 return needsPost; 288 } 289 290 // Intentionally not static, as no need to check thread on static methods 291 private void checkThread() { 292 if (!ThreadUtils.runningOnUiThread()) { 293 final RuntimeException threadViolation = createThreadException(); 294 ThreadUtils.postOnUiThread(new Runnable() { 295 @Override 296 public void run() { 297 throw threadViolation; 298 } 299 }); 300 throw createThreadException(); 301 } 302 } 303 304 @Override 305 public void setHorizontalScrollbarOverlay(final boolean overlay) { 306 if (checkNeedsPost()) { 307 mRunQueue.addTask(new Runnable() { 308 @Override 309 public void run() { 310 setHorizontalScrollbarOverlay(overlay); 311 } 312 }); 313 return; 314 } 315 mAwContents.setHorizontalScrollbarOverlay(overlay); 316 } 317 318 @Override 319 public void setVerticalScrollbarOverlay(final boolean overlay) { 320 if (checkNeedsPost()) { 321 mRunQueue.addTask(new Runnable() { 322 @Override 323 public void run() { 324 setVerticalScrollbarOverlay(overlay); 325 } 326 }); 327 return; 328 } 329 mAwContents.setVerticalScrollbarOverlay(overlay); 330 } 331 332 @Override 333 public boolean overlayHorizontalScrollbar() { 334 mFactory.startYourEngines(false); 335 if (checkNeedsPost()) { 336 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 337 @Override 338 public Boolean call() { 339 return overlayHorizontalScrollbar(); 340 } 341 }); 342 return ret; 343 } 344 return mAwContents.overlayHorizontalScrollbar(); 345 } 346 347 @Override 348 public boolean overlayVerticalScrollbar() { 349 mFactory.startYourEngines(false); 350 if (checkNeedsPost()) { 351 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 352 @Override 353 public Boolean call() { 354 return overlayVerticalScrollbar(); 355 } 356 }); 357 return ret; 358 } 359 return mAwContents.overlayVerticalScrollbar(); 360 } 361 362 @Override 363 public int getVisibleTitleHeight() { 364 // This is deprecated in WebView and should always return 0. 365 return 0; 366 } 367 368 @Override 369 public SslCertificate getCertificate() { 370 mFactory.startYourEngines(true); 371 if (checkNeedsPost()) { 372 SslCertificate ret = runOnUiThreadBlocking(new Callable<SslCertificate>() { 373 @Override 374 public SslCertificate call() { 375 return getCertificate(); 376 } 377 }); 378 return ret; 379 } 380 return mAwContents.getCertificate(); 381 } 382 383 @Override 384 public void setCertificate(SslCertificate certificate) { 385 // intentional no-op 386 } 387 388 @Override 389 public void savePassword(String host, String username, String password) { 390 // This is a deprecated API: intentional no-op. 391 } 392 393 @Override 394 public void setHttpAuthUsernamePassword(final String host, final String realm, 395 final String username, final String password) { 396 if (checkNeedsPost()) { 397 mRunQueue.addTask(new Runnable() { 398 @Override 399 public void run() { 400 setHttpAuthUsernamePassword(host, realm, username, password); 401 } 402 }); 403 return; 404 } 405 mAwContents.setHttpAuthUsernamePassword(host, realm, username, password); 406 } 407 408 @Override 409 public String[] getHttpAuthUsernamePassword(final String host, final String realm) { 410 mFactory.startYourEngines(true); 411 if (checkNeedsPost()) { 412 String[] ret = runOnUiThreadBlocking(new Callable<String[]>() { 413 @Override 414 public String[] call() { 415 return getHttpAuthUsernamePassword(host, realm); 416 } 417 }); 418 return ret; 419 } 420 return mAwContents.getHttpAuthUsernamePassword(host, realm); 421 } 422 423 @Override 424 public void destroy() { 425 if (checkNeedsPost()) { 426 mRunQueue.addTask(new Runnable() { 427 @Override 428 public void run() { 429 destroy(); 430 } 431 }); 432 return; 433 } 434 435 mAwContents.destroy(); 436 if (mGLfunctor != null) { 437 mGLfunctor.destroy(); 438 mGLfunctor = null; 439 } 440 } 441 442 @Override 443 public void setNetworkAvailable(final boolean networkUp) { 444 // Note that this purely toggles the JS navigator.online property. 445 // It does not in affect chromium or network stack state in any way. 446 if (checkNeedsPost()) { 447 mRunQueue.addTask(new Runnable() { 448 @Override 449 public void run() { 450 setNetworkAvailable(networkUp); 451 } 452 }); 453 return; 454 } 455 mAwContents.setNetworkAvailable(networkUp); 456 } 457 458 @Override 459 public WebBackForwardList saveState(final Bundle outState) { 460 mFactory.startYourEngines(true); 461 if (checkNeedsPost()) { 462 WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() { 463 @Override 464 public WebBackForwardList call() { 465 return saveState(outState); 466 } 467 }); 468 return ret; 469 } 470 if (outState == null) return null; 471 if (!mAwContents.saveState(outState)) return null; 472 return copyBackForwardList(); 473 } 474 475 @Override 476 public boolean savePicture(Bundle b, File dest) { 477 // Intentional no-op: hidden method on WebView. 478 return false; 479 } 480 481 @Override 482 public boolean restorePicture(Bundle b, File src) { 483 // Intentional no-op: hidden method on WebView. 484 return false; 485 } 486 487 @Override 488 public WebBackForwardList restoreState(final Bundle inState) { 489 mFactory.startYourEngines(true); 490 if (checkNeedsPost()) { 491 WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() { 492 @Override 493 public WebBackForwardList call() { 494 return restoreState(inState); 495 } 496 }); 497 return ret; 498 } 499 if (inState == null) return null; 500 if (!mAwContents.restoreState(inState)) return null; 501 return copyBackForwardList(); 502 } 503 504 @Override 505 public void loadUrl(final String url, Map<String, String> additionalHttpHeaders) { 506 // TODO: We may actually want to do some sanity checks here (like filter about://chrome). 507 508 // For backwards compatibility, apps targeting less than K will have JS URLs evaluated 509 // directly and any result of the evaluation will not replace the current page content. 510 // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will 511 // have the result of that URL replace the content of the current page. 512 final String JAVASCRIPT_SCHEME = "javascript:"; 513 if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT && 514 url != null && url.startsWith(JAVASCRIPT_SCHEME)) { 515 mFactory.startYourEngines(true); 516 if (checkNeedsPost()) { 517 mRunQueue.addTask(new Runnable() { 518 @Override 519 public void run() { 520 mAwContents.evaluateJavaScriptEvenIfNotYetNavigated( 521 url.substring(JAVASCRIPT_SCHEME.length())); 522 } 523 }); 524 } else { 525 mAwContents.evaluateJavaScriptEvenIfNotYetNavigated( 526 url.substring(JAVASCRIPT_SCHEME.length())); 527 } 528 return; 529 } 530 531 LoadUrlParams params = new LoadUrlParams(url); 532 if (additionalHttpHeaders != null) params.setExtraHeaders(additionalHttpHeaders); 533 loadUrlOnUiThread(params); 534 } 535 536 @Override 537 public void loadUrl(String url) { 538 // Early out to match old WebView implementation 539 if (url == null) { 540 return; 541 } 542 loadUrl(url, null); 543 } 544 545 @Override 546 public void postUrl(String url, byte[] postData) { 547 LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData); 548 Map<String,String> headers = new HashMap<String,String>(); 549 headers.put("Content-Type", "application/x-www-form-urlencoded"); 550 params.setExtraHeaders(headers); 551 loadUrlOnUiThread(params); 552 } 553 554 private static String fixupMimeType(String mimeType) { 555 return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType; 556 } 557 558 private static String fixupData(String data) { 559 return TextUtils.isEmpty(data) ? "" : data; 560 } 561 562 private static String fixupBase(String url) { 563 return TextUtils.isEmpty(url) ? "about:blank" : url; 564 } 565 566 private static String fixupHistory(String url) { 567 return TextUtils.isEmpty(url) ? "about:blank" : url; 568 } 569 570 private static boolean isBase64Encoded(String encoding) { 571 return "base64".equals(encoding); 572 } 573 574 @Override 575 public void loadData(String data, String mimeType, String encoding) { 576 loadUrlOnUiThread(LoadUrlParams.createLoadDataParams( 577 fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding))); 578 } 579 580 @Override 581 public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, 582 String historyUrl) { 583 data = fixupData(data); 584 mimeType = fixupMimeType(mimeType); 585 LoadUrlParams loadUrlParams; 586 baseUrl = fixupBase(baseUrl); 587 historyUrl = fixupHistory(historyUrl); 588 589 if (baseUrl.startsWith("data:")) { 590 // For backwards compatibility with WebViewClassic, we use the value of |encoding| 591 // as the charset, as long as it's not "base64". 592 boolean isBase64 = isBase64Encoded(encoding); 593 loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl( 594 data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding); 595 } else { 596 // When loading data with a non-data: base URL, the classic WebView would effectively 597 // "dump" that string of data into the WebView without going through regular URL 598 // loading steps such as decoding URL-encoded entities. We achieve this same behavior by 599 // base64 encoding the data that is passed here and then loading that as a data: URL. 600 try { 601 loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl( 602 Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType, 603 true, baseUrl, historyUrl, "utf-8"); 604 } catch (java.io.UnsupportedEncodingException e) { 605 Log.wtf(TAG, "Unable to load data string " + data, e); 606 return; 607 } 608 } 609 loadUrlOnUiThread(loadUrlParams); 610 } 611 612 private void loadUrlOnUiThread(final LoadUrlParams loadUrlParams) { 613 // This is the last point that we can delay starting the Chromium backend up 614 // and if the app has not caused us to bind the Chromium UI thread to a background thread 615 // we now bind Chromium's notion of the UI thread to the app main thread. 616 mFactory.startYourEngines(true); 617 if (checkNeedsPost()) { 618 // Disallowed in WebView API for apps targetting a new SDK 619 assert mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2; 620 mRunQueue.addTask(new Runnable() { 621 @Override 622 public void run() { 623 mAwContents.loadUrl(loadUrlParams); 624 } 625 }); 626 return; 627 } 628 mAwContents.loadUrl(loadUrlParams); 629 } 630 631 public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) { 632 checkThread(); 633 mAwContents.evaluateJavaScript(script, resultCallback); 634 } 635 636 @Override 637 public void saveWebArchive(String filename) { 638 saveWebArchive(filename, false, null); 639 } 640 641 @Override 642 public void saveWebArchive(final String basename, final boolean autoname, 643 final ValueCallback<String> callback) { 644 if (checkNeedsPost()) { 645 mRunQueue.addTask(new Runnable() { 646 @Override 647 public void run() { 648 saveWebArchive(basename, autoname, callback); 649 } 650 }); 651 return; 652 } 653 mAwContents.saveWebArchive(basename, autoname, callback); 654 } 655 656 @Override 657 public void stopLoading() { 658 if (checkNeedsPost()) { 659 mRunQueue.addTask(new Runnable() { 660 @Override 661 public void run() { 662 stopLoading(); 663 } 664 }); 665 return; 666 } 667 668 mAwContents.stopLoading(); 669 } 670 671 @Override 672 public void reload() { 673 if (checkNeedsPost()) { 674 mRunQueue.addTask(new Runnable() { 675 @Override 676 public void run() { 677 reload(); 678 } 679 }); 680 return; 681 } 682 mAwContents.reload(); 683 } 684 685 @Override 686 public boolean canGoBack() { 687 mFactory.startYourEngines(true); 688 if (checkNeedsPost()) { 689 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 690 @Override 691 public Boolean call() { 692 return canGoBack(); 693 } 694 }); 695 return ret; 696 } 697 return mAwContents.canGoBack(); 698 } 699 700 @Override 701 public void goBack() { 702 if (checkNeedsPost()) { 703 mRunQueue.addTask(new Runnable() { 704 @Override 705 public void run() { 706 goBack(); 707 } 708 }); 709 return; 710 } 711 mAwContents.goBack(); 712 } 713 714 @Override 715 public boolean canGoForward() { 716 mFactory.startYourEngines(true); 717 if (checkNeedsPost()) { 718 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 719 @Override 720 public Boolean call() { 721 return canGoForward(); 722 } 723 }); 724 return ret; 725 } 726 return mAwContents.canGoForward(); 727 } 728 729 @Override 730 public void goForward() { 731 if (checkNeedsPost()) { 732 mRunQueue.addTask(new Runnable() { 733 @Override 734 public void run() { 735 goForward(); 736 } 737 }); 738 return; 739 } 740 mAwContents.goForward(); 741 } 742 743 @Override 744 public boolean canGoBackOrForward(final int steps) { 745 mFactory.startYourEngines(true); 746 if (checkNeedsPost()) { 747 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 748 @Override 749 public Boolean call() { 750 return canGoBackOrForward(steps); 751 } 752 }); 753 return ret; 754 } 755 return mAwContents.canGoBackOrForward(steps); 756 } 757 758 @Override 759 public void goBackOrForward(final int steps) { 760 if (checkNeedsPost()) { 761 mRunQueue.addTask(new Runnable() { 762 @Override 763 public void run() { 764 goBackOrForward(steps); 765 } 766 }); 767 return; 768 } 769 mAwContents.goBackOrForward(steps); 770 } 771 772 @Override 773 public boolean isPrivateBrowsingEnabled() { 774 // Not supported in this WebView implementation. 775 return false; 776 } 777 778 @Override 779 public boolean pageUp(final boolean top) { 780 mFactory.startYourEngines(true); 781 if (checkNeedsPost()) { 782 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 783 @Override 784 public Boolean call() { 785 return pageUp(top); 786 } 787 }); 788 return ret; 789 } 790 return mAwContents.pageUp(top); 791 } 792 793 @Override 794 public boolean pageDown(final boolean bottom) { 795 mFactory.startYourEngines(true); 796 if (checkNeedsPost()) { 797 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 798 @Override 799 public Boolean call() { 800 return pageDown(bottom); 801 } 802 }); 803 return ret; 804 } 805 return mAwContents.pageDown(bottom); 806 } 807 808 @Override 809 public void clearView() { 810 if (checkNeedsPost()) { 811 mRunQueue.addTask(new Runnable() { 812 @Override 813 public void run() { 814 clearView(); 815 } 816 }); 817 return; 818 } 819 mAwContents.clearView(); 820 } 821 822 @Override 823 public Picture capturePicture() { 824 mFactory.startYourEngines(true); 825 if (checkNeedsPost()) { 826 Picture ret = runOnUiThreadBlocking(new Callable<Picture>() { 827 @Override 828 public Picture call() { 829 return capturePicture(); 830 } 831 }); 832 return ret; 833 } 834 return mAwContents.capturePicture(); 835 } 836 837 @Override 838 public float getScale() { 839 // No checkThread() as it is mostly thread safe (workaround for b/10652991). 840 mFactory.startYourEngines(true); 841 return mAwContents.getScale(); 842 } 843 844 @Override 845 public void setInitialScale(final int scaleInPercent) { 846 // No checkThread() as it is thread safe 847 mWebSettings.getAwSettings().setInitialPageScale(scaleInPercent); 848 } 849 850 @Override 851 public void invokeZoomPicker() { 852 if (checkNeedsPost()) { 853 mRunQueue.addTask(new Runnable() { 854 @Override 855 public void run() { 856 invokeZoomPicker(); 857 } 858 }); 859 return; 860 } 861 mAwContents.invokeZoomPicker(); 862 } 863 864 @Override 865 public WebView.HitTestResult getHitTestResult() { 866 mFactory.startYourEngines(true); 867 if (checkNeedsPost()) { 868 WebView.HitTestResult ret = runOnUiThreadBlocking( 869 new Callable<WebView.HitTestResult>() { 870 @Override 871 public WebView.HitTestResult call() { 872 return getHitTestResult(); 873 } 874 }); 875 return ret; 876 } 877 AwContents.HitTestData data = mAwContents.getLastHitTestResult(); 878 mHitTestResult.setType(data.hitTestResultType); 879 mHitTestResult.setExtra(data.hitTestResultExtraData); 880 return mHitTestResult; 881 } 882 883 @Override 884 public void requestFocusNodeHref(final Message hrefMsg) { 885 if (checkNeedsPost()) { 886 mRunQueue.addTask(new Runnable() { 887 @Override 888 public void run() { 889 requestFocusNodeHref(hrefMsg); 890 } 891 }); 892 return; 893 } 894 mAwContents.requestFocusNodeHref(hrefMsg); 895 } 896 897 @Override 898 public void requestImageRef(final Message msg) { 899 if (checkNeedsPost()) { 900 mRunQueue.addTask(new Runnable() { 901 @Override 902 public void run() { 903 requestImageRef(msg); 904 } 905 }); 906 return; 907 } 908 mAwContents.requestImageRef(msg); 909 } 910 911 @Override 912 public String getUrl() { 913 mFactory.startYourEngines(true); 914 if (checkNeedsPost()) { 915 String ret = runOnUiThreadBlocking(new Callable<String>() { 916 @Override 917 public String call() { 918 return getUrl(); 919 } 920 }); 921 return ret; 922 } 923 String url = mAwContents.getUrl(); 924 if (url == null || url.trim().isEmpty()) return null; 925 return url; 926 } 927 928 @Override 929 public String getOriginalUrl() { 930 mFactory.startYourEngines(true); 931 if (checkNeedsPost()) { 932 String ret = runOnUiThreadBlocking(new Callable<String>() { 933 @Override 934 public String call() { 935 return getOriginalUrl(); 936 } 937 }); 938 return ret; 939 } 940 String url = mAwContents.getOriginalUrl(); 941 if (url == null || url.trim().isEmpty()) return null; 942 return url; 943 } 944 945 @Override 946 public String getTitle() { 947 mFactory.startYourEngines(true); 948 if (checkNeedsPost()) { 949 String ret = runOnUiThreadBlocking(new Callable<String>() { 950 @Override 951 public String call() { 952 return getTitle(); 953 } 954 }); 955 return ret; 956 } 957 return mAwContents.getTitle(); 958 } 959 960 @Override 961 public Bitmap getFavicon() { 962 mFactory.startYourEngines(true); 963 if (checkNeedsPost()) { 964 Bitmap ret = runOnUiThreadBlocking(new Callable<Bitmap>() { 965 @Override 966 public Bitmap call() { 967 return getFavicon(); 968 } 969 }); 970 return ret; 971 } 972 return mAwContents.getFavicon(); 973 } 974 975 @Override 976 public String getTouchIconUrl() { 977 // Intentional no-op: hidden method on WebView. 978 return null; 979 } 980 981 @Override 982 public int getProgress() { 983 if (mAwContents == null) return 100; 984 // No checkThread() because the value is cached java side (workaround for b/10533304). 985 return mAwContents.getMostRecentProgress(); 986 } 987 988 @Override 989 public int getContentHeight() { 990 if (mAwContents == null) return 0; 991 // No checkThread() as it is mostly thread safe (workaround for b/10594869). 992 return mAwContents.getContentHeightCss(); 993 } 994 995 @Override 996 public int getContentWidth() { 997 if (mAwContents == null) return 0; 998 // No checkThread() as it is mostly thread safe (workaround for b/10594869). 999 return mAwContents.getContentWidthCss(); 1000 } 1001 1002 @Override 1003 public void pauseTimers() { 1004 if (checkNeedsPost()) { 1005 mRunQueue.addTask(new Runnable() { 1006 @Override 1007 public void run() { 1008 pauseTimers(); 1009 } 1010 }); 1011 return; 1012 } 1013 mAwContents.pauseTimers(); 1014 } 1015 1016 @Override 1017 public void resumeTimers() { 1018 if (checkNeedsPost()) { 1019 mRunQueue.addTask(new Runnable() { 1020 @Override 1021 public void run() { 1022 resumeTimers(); 1023 } 1024 }); 1025 return; 1026 } 1027 mAwContents.resumeTimers(); 1028 } 1029 1030 @Override 1031 public void onPause() { 1032 if (checkNeedsPost()) { 1033 mRunQueue.addTask(new Runnable() { 1034 @Override 1035 public void run() { 1036 onPause(); 1037 } 1038 }); 1039 return; 1040 } 1041 mAwContents.onPause(); 1042 } 1043 1044 @Override 1045 public void onResume() { 1046 if (checkNeedsPost()) { 1047 mRunQueue.addTask(new Runnable() { 1048 @Override 1049 public void run() { 1050 onResume(); 1051 } 1052 }); 1053 return; 1054 } 1055 mAwContents.onResume(); 1056 } 1057 1058 @Override 1059 public boolean isPaused() { 1060 mFactory.startYourEngines(true); 1061 if (checkNeedsPost()) { 1062 Boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1063 @Override 1064 public Boolean call() { 1065 return isPaused(); 1066 } 1067 }); 1068 return ret; 1069 } 1070 return mAwContents.isPaused(); 1071 } 1072 1073 @Override 1074 public void freeMemory() { 1075 // Intentional no-op. Memory is managed automatically by Chromium. 1076 } 1077 1078 @Override 1079 public void clearCache(final boolean includeDiskFiles) { 1080 if (checkNeedsPost()) { 1081 mRunQueue.addTask(new Runnable() { 1082 @Override 1083 public void run() { 1084 clearCache(includeDiskFiles); 1085 } 1086 }); 1087 return; 1088 } 1089 mAwContents.clearCache(includeDiskFiles); 1090 } 1091 1092 /** 1093 * This is a poorly named method, but we keep it for historical reasons. 1094 */ 1095 @Override 1096 public void clearFormData() { 1097 if (checkNeedsPost()) { 1098 mRunQueue.addTask(new Runnable() { 1099 @Override 1100 public void run() { 1101 clearFormData(); 1102 } 1103 }); 1104 return; 1105 } 1106 mAwContents.hideAutofillPopup(); 1107 } 1108 1109 @Override 1110 public void clearHistory() { 1111 if (checkNeedsPost()) { 1112 mRunQueue.addTask(new Runnable() { 1113 @Override 1114 public void run() { 1115 clearHistory(); 1116 } 1117 }); 1118 return; 1119 } 1120 mAwContents.clearHistory(); 1121 } 1122 1123 @Override 1124 public void clearSslPreferences() { 1125 if (checkNeedsPost()) { 1126 mRunQueue.addTask(new Runnable() { 1127 @Override 1128 public void run() { 1129 clearSslPreferences(); 1130 } 1131 }); 1132 return; 1133 } 1134 mAwContents.clearSslPreferences(); 1135 } 1136 1137 @Override 1138 public void clearClientCertPreferences(final ValueCallback<Void> resultCallback) { 1139 if (checkNeedsPost()) { 1140 mRunQueue.addTask(new Runnable() { 1141 @Override 1142 public void run() { 1143 clearClientCertPreferences(resultCallback); 1144 } 1145 }); 1146 return; 1147 } 1148 // TODO(sgurun) plumb resultCallback after adding the change in chromium side. 1149 mAwContents.clearClientCertPreferences(); 1150 } 1151 1152 @Override 1153 public WebBackForwardList copyBackForwardList() { 1154 mFactory.startYourEngines(true); 1155 if (checkNeedsPost()) { 1156 WebBackForwardList ret = runOnUiThreadBlocking(new Callable<WebBackForwardList>() { 1157 @Override 1158 public WebBackForwardList call() { 1159 return copyBackForwardList(); 1160 } 1161 }); 1162 return ret; 1163 } 1164 return new WebBackForwardListChromium( 1165 mAwContents.getNavigationHistory()); 1166 } 1167 1168 @Override 1169 public void setFindListener(WebView.FindListener listener) { 1170 mContentsClientAdapter.setFindListener(listener); 1171 } 1172 1173 @Override 1174 public void findNext(final boolean forwards) { 1175 if (checkNeedsPost()) { 1176 mRunQueue.addTask(new Runnable() { 1177 @Override 1178 public void run() { 1179 findNext(forwards); 1180 } 1181 }); 1182 return; 1183 } 1184 mAwContents.findNext(forwards); 1185 } 1186 1187 @Override 1188 public int findAll(final String searchString) { 1189 findAllAsync(searchString); 1190 return 0; 1191 } 1192 1193 @Override 1194 public void findAllAsync(final String searchString) { 1195 if (checkNeedsPost()) { 1196 mRunQueue.addTask(new Runnable() { 1197 @Override 1198 public void run() { 1199 findAllAsync(searchString); 1200 } 1201 }); 1202 return; 1203 } 1204 mAwContents.findAllAsync(searchString); 1205 } 1206 1207 @Override 1208 public boolean showFindDialog(final String text, final boolean showIme) { 1209 mFactory.startYourEngines(false); 1210 if (checkNeedsPost()) { 1211 return false; 1212 } 1213 if (mWebView.getParent() == null) { 1214 return false; 1215 } 1216 1217 FindActionModeCallback findAction = new FindActionModeCallback(mWebView.getContext()); 1218 if (findAction == null) { 1219 return false; 1220 } 1221 1222 mWebView.startActionMode(findAction); 1223 findAction.setWebView(mWebView); 1224 if (showIme) { 1225 findAction.showSoftInput(); 1226 } 1227 1228 if (text != null) { 1229 findAction.setText(text); 1230 findAction.findAll(); 1231 } 1232 1233 return true; 1234 } 1235 1236 @Override 1237 public void notifyFindDialogDismissed() { 1238 if (checkNeedsPost()) { 1239 mRunQueue.addTask(new Runnable() { 1240 @Override 1241 public void run() { 1242 notifyFindDialogDismissed(); 1243 } 1244 }); 1245 return; 1246 } 1247 clearMatches(); 1248 } 1249 1250 @Override 1251 public void clearMatches() { 1252 if (checkNeedsPost()) { 1253 mRunQueue.addTask(new Runnable() { 1254 @Override 1255 public void run() { 1256 clearMatches(); 1257 } 1258 }); 1259 return; 1260 } 1261 mAwContents.clearMatches(); 1262 } 1263 1264 @Override 1265 public void documentHasImages(final Message response) { 1266 if (checkNeedsPost()) { 1267 mRunQueue.addTask(new Runnable() { 1268 @Override 1269 public void run() { 1270 documentHasImages(response); 1271 } 1272 }); 1273 return; 1274 } 1275 mAwContents.documentHasImages(response); 1276 } 1277 1278 @Override 1279 public void setWebViewClient(WebViewClient client) { 1280 mContentsClientAdapter.setWebViewClient(client); 1281 } 1282 1283 @Override 1284 public void setDownloadListener(DownloadListener listener) { 1285 mContentsClientAdapter.setDownloadListener(listener); 1286 } 1287 1288 @Override 1289 public void setWebChromeClient(WebChromeClient client) { 1290 mContentsClientAdapter.setWebChromeClient(client); 1291 } 1292 1293 @Override 1294 public void setPictureListener(final WebView.PictureListener listener) { 1295 if (checkNeedsPost()) { 1296 mRunQueue.addTask(new Runnable() { 1297 @Override 1298 public void run() { 1299 setPictureListener(listener); 1300 } 1301 }); 1302 return; 1303 } 1304 mContentsClientAdapter.setPictureListener(listener); 1305 mAwContents.enableOnNewPicture(listener != null, 1306 mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2); 1307 } 1308 1309 @Override 1310 public void addJavascriptInterface(final Object obj, final String interfaceName) { 1311 if (checkNeedsPost()) { 1312 mRunQueue.addTask(new Runnable() { 1313 @Override 1314 public void run() { 1315 addJavascriptInterface(obj, interfaceName); 1316 } 1317 }); 1318 return; 1319 } 1320 Class<? extends Annotation> requiredAnnotation = null; 1321 if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 1322 requiredAnnotation = JavascriptInterface.class; 1323 } 1324 mAwContents.addPossiblyUnsafeJavascriptInterface(obj, interfaceName, requiredAnnotation); 1325 } 1326 1327 @Override 1328 public void removeJavascriptInterface(final String interfaceName) { 1329 if (checkNeedsPost()) { 1330 mRunQueue.addTask(new Runnable() { 1331 @Override 1332 public void run() { 1333 removeJavascriptInterface(interfaceName); 1334 } 1335 }); 1336 return; 1337 } 1338 mAwContents.removeJavascriptInterface(interfaceName); 1339 } 1340 1341 @Override 1342 public WebSettings getSettings() { 1343 return mWebSettings; 1344 } 1345 1346 @Override 1347 public void setMapTrackballToArrowKeys(boolean setMap) { 1348 // This is a deprecated API: intentional no-op. 1349 } 1350 1351 @Override 1352 public void flingScroll(final int vx, final int vy) { 1353 if (checkNeedsPost()) { 1354 mRunQueue.addTask(new Runnable() { 1355 @Override 1356 public void run() { 1357 flingScroll(vx, vy); 1358 } 1359 }); 1360 return; 1361 } 1362 mAwContents.flingScroll(vx, vy); 1363 } 1364 1365 @Override 1366 public View getZoomControls() { 1367 mFactory.startYourEngines(false); 1368 if (checkNeedsPost()) { 1369 return null; 1370 } 1371 1372 // This was deprecated in 2009 and hidden in JB MR1, so just provide the minimum needed 1373 // to stop very out-dated applications from crashing. 1374 Log.w(TAG, "WebView doesn't support getZoomControls"); 1375 return mAwContents.getSettings().supportZoom() ? new View(mWebView.getContext()) : null; 1376 } 1377 1378 @Override 1379 public boolean canZoomIn() { 1380 if (checkNeedsPost()) { 1381 return false; 1382 } 1383 return mAwContents.canZoomIn(); 1384 } 1385 1386 @Override 1387 public boolean canZoomOut() { 1388 if (checkNeedsPost()) { 1389 return false; 1390 } 1391 return mAwContents.canZoomOut(); 1392 } 1393 1394 @Override 1395 public boolean zoomIn() { 1396 mFactory.startYourEngines(true); 1397 if (checkNeedsPost()) { 1398 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1399 @Override 1400 public Boolean call() { 1401 return zoomIn(); 1402 } 1403 }); 1404 return ret; 1405 } 1406 return mAwContents.zoomIn(); 1407 } 1408 1409 @Override 1410 public boolean zoomOut() { 1411 mFactory.startYourEngines(true); 1412 if (checkNeedsPost()) { 1413 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1414 @Override 1415 public Boolean call() { 1416 return zoomOut(); 1417 } 1418 }); 1419 return ret; 1420 } 1421 return mAwContents.zoomOut(); 1422 } 1423 1424 @Override 1425 public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) { 1426 // Intentional no-op 1427 } 1428 1429 @Override 1430 public View findHierarchyView(String className, int hashCode) { 1431 // Intentional no-op 1432 return null; 1433 } 1434 1435 // WebViewProvider glue methods --------------------------------------------------------------- 1436 1437 @Override 1438 // This needs to be kept thread safe! 1439 public WebViewProvider.ViewDelegate getViewDelegate() { 1440 return this; 1441 } 1442 1443 @Override 1444 // This needs to be kept thread safe! 1445 public WebViewProvider.ScrollDelegate getScrollDelegate() { 1446 return this; 1447 } 1448 1449 1450 // WebViewProvider.ViewDelegate implementation ------------------------------------------------ 1451 1452 // TODO: remove from WebViewProvider and use default implementation from 1453 // ViewGroup. 1454 // @Override 1455 public boolean shouldDelayChildPressedState() { 1456 mFactory.startYourEngines(false); 1457 if (checkNeedsPost()) { 1458 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1459 @Override 1460 public Boolean call() { 1461 return shouldDelayChildPressedState(); 1462 } 1463 }); 1464 return ret; 1465 } 1466 return true; 1467 } 1468 1469// @Override 1470 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 1471 mFactory.startYourEngines(false); 1472 if (checkNeedsPost()) { 1473 AccessibilityNodeProvider ret = runOnUiThreadBlocking( 1474 new Callable<AccessibilityNodeProvider>() { 1475 @Override 1476 public AccessibilityNodeProvider call() { 1477 return getAccessibilityNodeProvider(); 1478 } 1479 }); 1480 return ret; 1481 } 1482 return mAwContents.getAccessibilityNodeProvider(); 1483 } 1484 1485 @Override 1486 public void onInitializeAccessibilityNodeInfo(final AccessibilityNodeInfo info) { 1487 mFactory.startYourEngines(false); 1488 if (checkNeedsPost()) { 1489 runVoidTaskOnUiThreadBlocking(new Runnable() { 1490 @Override 1491 public void run() { 1492 onInitializeAccessibilityNodeInfo(info); 1493 } 1494 }); 1495 return; 1496 } 1497 mAwContents.onInitializeAccessibilityNodeInfo(info); 1498 } 1499 1500 @Override 1501 public void onInitializeAccessibilityEvent(final AccessibilityEvent event) { 1502 mFactory.startYourEngines(false); 1503 if (checkNeedsPost()) { 1504 runVoidTaskOnUiThreadBlocking(new Runnable() { 1505 @Override 1506 public void run() { 1507 onInitializeAccessibilityEvent(event); 1508 } 1509 }); 1510 return; 1511 } 1512 mAwContents.onInitializeAccessibilityEvent(event); 1513 } 1514 1515 @Override 1516 public boolean performAccessibilityAction(final int action, final Bundle arguments) { 1517 mFactory.startYourEngines(false); 1518 if (checkNeedsPost()) { 1519 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1520 @Override 1521 public Boolean call() { 1522 return performAccessibilityAction(action, arguments); 1523 } 1524 }); 1525 return ret; 1526 } 1527 if (mAwContents.supportsAccessibilityAction(action)) { 1528 return mAwContents.performAccessibilityAction(action, arguments); 1529 } 1530 return mWebViewPrivate.super_performAccessibilityAction(action, arguments); 1531 } 1532 1533 @Override 1534 public void setOverScrollMode(final int mode) { 1535 // This gets called from the android.view.View c'tor that WebView inherits from. This 1536 // causes the method to be called when mAwContents == null. 1537 // It's safe to ignore these calls however since AwContents will read the current value of 1538 // this setting when it's created. 1539 if (mAwContents == null) return; 1540 1541 if (checkNeedsPost()) { 1542 mRunQueue.addTask(new Runnable() { 1543 @Override 1544 public void run() { 1545 setOverScrollMode(mode); 1546 } 1547 }); 1548 return; 1549 } 1550 mAwContents.setOverScrollMode(mode); 1551 } 1552 1553 @Override 1554 public void setScrollBarStyle(final int style) { 1555 if (checkNeedsPost()) { 1556 mRunQueue.addTask(new Runnable() { 1557 @Override 1558 public void run() { 1559 setScrollBarStyle(style); 1560 } 1561 }); 1562 return; 1563 } 1564 mAwContents.setScrollBarStyle(style); 1565 } 1566 1567 @Override 1568 public void onDrawVerticalScrollBar(final Canvas canvas, final Drawable scrollBar, final int l, 1569 final int t, final int r, final int b) { 1570 // WebViewClassic was overriding this method to handle rubberband over-scroll. Since 1571 // WebViewChromium doesn't support that the vanilla implementation of this method can be 1572 // used. 1573 mWebViewPrivate.super_onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b); 1574 } 1575 1576 @Override 1577 public void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX, 1578 final boolean clampedY) { 1579 if (checkNeedsPost()) { 1580 mRunQueue.addTask(new Runnable() { 1581 @Override 1582 public void run() { 1583 onOverScrolled(scrollX, scrollY, clampedX, clampedY); 1584 } 1585 }); 1586 return; 1587 } 1588 mAwContents.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); 1589 } 1590 1591 @Override 1592 public void onWindowVisibilityChanged(final int visibility) { 1593 if (checkNeedsPost()) { 1594 mRunQueue.addTask(new Runnable() { 1595 @Override 1596 public void run() { 1597 onWindowVisibilityChanged(visibility); 1598 } 1599 }); 1600 return; 1601 } 1602 mAwContents.onWindowVisibilityChanged(visibility); 1603 } 1604 1605 @Override 1606 public void onDraw(final Canvas canvas) { 1607 mFactory.startYourEngines(true); 1608 if (checkNeedsPost()) { 1609 runVoidTaskOnUiThreadBlocking(new Runnable() { 1610 @Override 1611 public void run() { 1612 onDraw(canvas); 1613 } 1614 }); 1615 return; 1616 } 1617 mAwContents.onDraw(canvas); 1618 } 1619 1620 @Override 1621 public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) { 1622 // This API is our strongest signal from the View system that this 1623 // WebView is going to be bound to a View hierarchy and so at this 1624 // point we must bind Chromium's UI thread to the current thread. 1625 mFactory.startYourEngines(false); 1626 checkThread(); 1627 mWebViewPrivate.super_setLayoutParams(layoutParams); 1628 } 1629 1630 @Override 1631 public boolean performLongClick() { 1632 // Return false unless the WebView is attached to a View with a parent 1633 return mWebView.getParent() != null ? mWebViewPrivate.super_performLongClick() : false; 1634 } 1635 1636 @Override 1637 public void onConfigurationChanged(final Configuration newConfig) { 1638 if (checkNeedsPost()) { 1639 mRunQueue.addTask(new Runnable() { 1640 @Override 1641 public void run() { 1642 onConfigurationChanged(newConfig); 1643 } 1644 }); 1645 return; 1646 } 1647 mAwContents.onConfigurationChanged(newConfig); 1648 } 1649 1650 @Override 1651 public InputConnection onCreateInputConnection(final EditorInfo outAttrs) { 1652 mFactory.startYourEngines(false); 1653 if (checkNeedsPost()) { 1654 return null; 1655 } 1656 return mAwContents.onCreateInputConnection(outAttrs); 1657 } 1658 1659 @Override 1660 public boolean onKeyMultiple(final int keyCode, final int repeatCount, final KeyEvent event) { 1661 mFactory.startYourEngines(false); 1662 if (checkNeedsPost()) { 1663 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1664 @Override 1665 public Boolean call() { 1666 return onKeyMultiple(keyCode, repeatCount, event); 1667 } 1668 }); 1669 return ret; 1670 } 1671 UnimplementedWebViewApi.invoke(); 1672 return false; 1673 } 1674 1675 @Override 1676 public boolean onKeyDown(final int keyCode, final KeyEvent event) { 1677 mFactory.startYourEngines(false); 1678 if (checkNeedsPost()) { 1679 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1680 @Override 1681 public Boolean call() { 1682 return onKeyDown(keyCode, event); 1683 } 1684 }); 1685 return ret; 1686 } 1687 UnimplementedWebViewApi.invoke(); 1688 return false; 1689 } 1690 1691 @Override 1692 public boolean onKeyUp(final int keyCode, final KeyEvent event) { 1693 mFactory.startYourEngines(false); 1694 if (checkNeedsPost()) { 1695 boolean ret = runOnUiThreadBlocking(new Callable<Boolean>() { 1696 @Override 1697 public Boolean call() { 1698 return onKeyUp(keyCode, event); 1699 } 1700 }); 1701 return ret; 1702 } 1703 return mAwContents.onKeyUp(keyCode, event); 1704 } 1705 1706 @Override 1707 public void onAttachedToWindow() { 1708 // This API is our strongest signal from the View system that this 1709 // WebView is going to be bound to a View hierarchy and so at this 1710 // point we must bind Chromium's UI thread to the current thread. 1711 mFactory.startYourEngines(false); 1712 checkThread(); 1713 mAwContents.onAttachedToWindow(); 1714 } 1715 1716 @Override 1717 public void onDetachedFromWindow() { 1718 if (checkNeedsPost()) { 1719 mRunQueue.addTask(new Runnable() { 1720 @Override 1721 public void run() { 1722 onDetachedFromWindow(); 1723 } 1724 }); 1725 return; 1726 } 1727 1728 mAwContents.onDetachedFromWindow(); 1729 if (mGLfunctor != null) { 1730 mGLfunctor.detach(); 1731 } 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 // WebViewProvider.ScrollDelegate implementation ---------------------------------------------- 1956 1957 @Override 1958 public int computeHorizontalScrollRange() { 1959 mFactory.startYourEngines(false); 1960 if (checkNeedsPost()) { 1961 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 1962 @Override 1963 public Integer call() { 1964 return computeHorizontalScrollRange(); 1965 } 1966 }); 1967 return ret; 1968 } 1969 return mAwContents.computeHorizontalScrollRange(); 1970 } 1971 1972 @Override 1973 public int computeHorizontalScrollOffset() { 1974 mFactory.startYourEngines(false); 1975 if (checkNeedsPost()) { 1976 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 1977 @Override 1978 public Integer call() { 1979 return computeHorizontalScrollOffset(); 1980 } 1981 }); 1982 return ret; 1983 } 1984 return mAwContents.computeHorizontalScrollOffset(); 1985 } 1986 1987 @Override 1988 public int computeVerticalScrollRange() { 1989 mFactory.startYourEngines(false); 1990 if (checkNeedsPost()) { 1991 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 1992 @Override 1993 public Integer call() { 1994 return computeVerticalScrollRange(); 1995 } 1996 }); 1997 return ret; 1998 } 1999 return mAwContents.computeVerticalScrollRange(); 2000 } 2001 2002 @Override 2003 public int computeVerticalScrollOffset() { 2004 mFactory.startYourEngines(false); 2005 if (checkNeedsPost()) { 2006 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 2007 @Override 2008 public Integer call() { 2009 return computeVerticalScrollOffset(); 2010 } 2011 }); 2012 return ret; 2013 } 2014 return mAwContents.computeVerticalScrollOffset(); 2015 } 2016 2017 @Override 2018 public int computeVerticalScrollExtent() { 2019 mFactory.startYourEngines(false); 2020 if (checkNeedsPost()) { 2021 int ret = runOnUiThreadBlocking(new Callable<Integer>() { 2022 @Override 2023 public Integer call() { 2024 return computeVerticalScrollExtent(); 2025 } 2026 }); 2027 return ret; 2028 } 2029 return mAwContents.computeVerticalScrollExtent(); 2030 } 2031 2032 @Override 2033 public void computeScroll() { 2034 mFactory.startYourEngines(false); 2035 if (checkNeedsPost()) { 2036 runVoidTaskOnUiThreadBlocking(new Runnable() { 2037 @Override 2038 public void run() { 2039 computeScroll(); 2040 } 2041 }); 2042 return; 2043 } 2044 mAwContents.computeScroll(); 2045 } 2046 2047 @Override 2048 public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) { 2049 checkThread(); 2050 return new AwPrintDocumentAdapter(mAwContents.getPdfExporter(), documentName); 2051 } 2052 2053 public void preauthorizePermission(Uri origin, long resources) { 2054 // TODO: implement preauthorizePermission. 2055 } 2056 2057 // AwContents.InternalAccessDelegate implementation -------------------------------------- 2058 private class InternalAccessAdapter implements AwContents.InternalAccessDelegate { 2059 @Override 2060 public boolean drawChild(Canvas arg0, View arg1, long arg2) { 2061 UnimplementedWebViewApi.invoke(); 2062 return false; 2063 } 2064 2065 @Override 2066 public boolean super_onKeyUp(int arg0, KeyEvent arg1) { 2067 // Intentional no-op 2068 return false; 2069 } 2070 2071 @Override 2072 public boolean super_dispatchKeyEventPreIme(KeyEvent arg0) { 2073 UnimplementedWebViewApi.invoke(); 2074 return false; 2075 } 2076 2077 @Override 2078 public boolean super_dispatchKeyEvent(KeyEvent event) { 2079 return mWebViewPrivate.super_dispatchKeyEvent(event); 2080 } 2081 2082 @Override 2083 public boolean super_onGenericMotionEvent(MotionEvent arg0) { 2084 return mWebViewPrivate.super_onGenericMotionEvent(arg0); 2085 } 2086 2087 @Override 2088 public void super_onConfigurationChanged(Configuration arg0) { 2089 // Intentional no-op 2090 } 2091 2092 @Override 2093 public int super_getScrollBarStyle() { 2094 return mWebViewPrivate.super_getScrollBarStyle(); 2095 } 2096 2097 @Override 2098 public boolean awakenScrollBars() { 2099 mWebViewPrivate.awakenScrollBars(0); 2100 // TODO: modify the WebView.PrivateAccess to provide a return value. 2101 return true; 2102 } 2103 2104 @Override 2105 public boolean super_awakenScrollBars(int arg0, boolean arg1) { 2106 // TODO: need method on WebView.PrivateAccess? 2107 UnimplementedWebViewApi.invoke(); 2108 return false; 2109 } 2110 2111 @Override 2112 public void onScrollChanged(int l, int t, int oldl, int oldt) { 2113 // Intentional no-op. 2114 // Chromium calls this directly to trigger accessibility events. That isn't needed 2115 // for WebView since super_scrollTo invokes onScrollChanged for us. 2116 } 2117 2118 @Override 2119 public void overScrollBy(int deltaX, int deltaY, 2120 int scrollX, int scrollY, 2121 int scrollRangeX, int scrollRangeY, 2122 int maxOverScrollX, int maxOverScrollY, 2123 boolean isTouchEvent) { 2124 mWebViewPrivate.overScrollBy(deltaX, deltaY, scrollX, scrollY, 2125 scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 2126 } 2127 2128 @Override 2129 public void super_scrollTo(int scrollX, int scrollY) { 2130 mWebViewPrivate.super_scrollTo(scrollX, scrollY); 2131 } 2132 2133 @Override 2134 public void setMeasuredDimension(int measuredWidth, int measuredHeight) { 2135 mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight); 2136 } 2137 2138 @Override 2139 public boolean requestDrawGL(Canvas canvas) { 2140 if (mGLfunctor == null) { 2141 mGLfunctor = new DrawGLFunctor(mAwContents.getAwDrawGLViewContext()); 2142 } 2143 return mGLfunctor.requestDrawGL((HardwareCanvas)canvas, mWebView.getViewRootImpl()); 2144 } 2145 2146 // @Override 2147 public boolean executeHardwareAction(Runnable action) { 2148 return mWebView.executeHardwareAction(action); 2149 } 2150 } 2151} 2152