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