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