1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.android_webview; 6 7 import android.annotation.SuppressLint; 8 import android.app.Activity; 9 import android.content.ComponentCallbacks2; 10 import android.content.Context; 11 import android.content.res.Configuration; 12 import android.graphics.Bitmap; 13 import android.graphics.Canvas; 14 import android.graphics.Color; 15 import android.graphics.Paint; 16 import android.graphics.Picture; 17 import android.graphics.Rect; 18 import android.net.Uri; 19 import android.net.http.SslCertificate; 20 import android.os.AsyncTask; 21 import android.os.Build; 22 import android.os.Bundle; 23 import android.os.Handler; 24 import android.os.Message; 25 import android.text.TextUtils; 26 import android.util.Log; 27 import android.util.Pair; 28 import android.view.KeyEvent; 29 import android.view.MotionEvent; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.view.accessibility.AccessibilityEvent; 33 import android.view.accessibility.AccessibilityNodeInfo; 34 import android.view.accessibility.AccessibilityNodeProvider; 35 import android.view.inputmethod.EditorInfo; 36 import android.view.inputmethod.InputConnection; 37 import android.webkit.GeolocationPermissions; 38 import android.webkit.ValueCallback; 39 import android.widget.OverScroller; 40 41 import org.chromium.android_webview.permission.AwPermissionRequest; 42 import org.chromium.base.CalledByNative; 43 import org.chromium.base.JNINamespace; 44 import org.chromium.base.ThreadUtils; 45 import org.chromium.base.VisibleForTesting; 46 import org.chromium.components.navigation_interception.InterceptNavigationDelegate; 47 import org.chromium.components.navigation_interception.NavigationParams; 48 import org.chromium.content.browser.ContentSettings; 49 import org.chromium.content.browser.ContentViewClient; 50 import org.chromium.content.browser.ContentViewCore; 51 import org.chromium.content.browser.ContentViewStatics; 52 import org.chromium.content.browser.SmartClipProvider; 53 import org.chromium.content.browser.WebContentsObserverAndroid; 54 import org.chromium.content.common.CleanupReference; 55 import org.chromium.content_public.browser.GestureStateListener; 56 import org.chromium.content_public.browser.JavaScriptCallback; 57 import org.chromium.content_public.browser.LoadUrlParams; 58 import org.chromium.content_public.browser.NavigationController; 59 import org.chromium.content_public.browser.NavigationHistory; 60 import org.chromium.content_public.browser.WebContents; 61 import org.chromium.content_public.common.Referrer; 62 import org.chromium.ui.base.ActivityWindowAndroid; 63 import org.chromium.ui.base.PageTransitionTypes; 64 import org.chromium.ui.base.WindowAndroid; 65 import org.chromium.ui.gfx.DeviceDisplayInfo; 66 67 import java.io.File; 68 import java.lang.annotation.Annotation; 69 import java.net.MalformedURLException; 70 import java.net.URL; 71 import java.util.HashMap; 72 import java.util.Locale; 73 import java.util.Map; 74 import java.util.concurrent.Callable; 75 76 /** 77 * Exposes the native AwContents class, and together these classes wrap the ContentViewCore 78 * and Browser components that are required to implement Android WebView API. This is the 79 * primary entry point for the WebViewProvider implementation; it holds a 1:1 object 80 * relationship with application WebView instances. 81 * (We define this class independent of the hidden WebViewProvider interfaces, to allow 82 * continuous build & test in the open source SDK-based tree). 83 */ 84 @JNINamespace("android_webview") 85 public class AwContents implements SmartClipProvider { 86 private static final String TAG = "AwContents"; 87 88 private static final String WEB_ARCHIVE_EXTENSION = ".mht"; 89 90 // Used to avoid enabling zooming in / out if resulting zooming will 91 // produce little visible difference. 92 private static final float ZOOM_CONTROLS_EPSILON = 0.007f; 93 94 /** 95 * WebKit hit test related data strcutre. These are used to implement 96 * getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView. 97 * All values should be updated together. The native counterpart is 98 * AwHitTestData. 99 */ 100 public static class HitTestData { 101 // Used in getHitTestResult. 102 public int hitTestResultType; 103 public String hitTestResultExtraData; 104 105 // Used in requestFocusNodeHref (all three) and requestImageRef (only imgSrc). 106 public String href; 107 public String anchorText; 108 public String imgSrc; 109 } 110 111 /** 112 * Interface that consumers of {@link AwContents} must implement to allow the proper 113 * dispatching of view methods through the containing view. 114 */ 115 public interface InternalAccessDelegate extends ContentViewCore.InternalAccessDelegate { 116 117 /** 118 * @see View#overScrollBy(int, int, int, int, int, int, int, int, boolean); 119 */ overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)120 void overScrollBy(int deltaX, int deltaY, 121 int scrollX, int scrollY, 122 int scrollRangeX, int scrollRangeY, 123 int maxOverScrollX, int maxOverScrollY, 124 boolean isTouchEvent); 125 126 /** 127 * @see View#scrollTo(int, int) 128 */ super_scrollTo(int scrollX, int scrollY)129 void super_scrollTo(int scrollX, int scrollY); 130 131 /** 132 * @see View#setMeasuredDimension(int, int) 133 */ setMeasuredDimension(int measuredWidth, int measuredHeight)134 void setMeasuredDimension(int measuredWidth, int measuredHeight); 135 136 /** 137 * @see View#getScrollBarStyle() 138 */ super_getScrollBarStyle()139 int super_getScrollBarStyle(); 140 } 141 142 /** 143 * Interface that consumers of {@link AwContents} must implement to support 144 * native GL rendering. 145 */ 146 public interface NativeGLDelegate { 147 /** 148 * Requests a callback on the native DrawGL method (see getAwDrawGLFunction) 149 * if called from within onDraw, |canvas| will be non-null and hardware accelerated. 150 * Otherwise, |canvas| will be null, and the container view itself will be hardware 151 * accelerated. If |waitForCompletion| is true, this method will not return until 152 * functor has returned. 153 * Should avoid setting |waitForCompletion| when |canvas| is not null. 154 * |containerView| is the view where the AwContents should be drawn. 155 * 156 * @return false indicates the GL draw request was not accepted, and the caller 157 * should fallback to the SW path. 158 */ requestDrawGL(Canvas canvas, boolean waitForCompletion, View containerView)159 boolean requestDrawGL(Canvas canvas, boolean waitForCompletion, View containerView); 160 161 /** 162 * Detaches the GLFunctor from the view tree. 163 */ detachGLFunctor()164 void detachGLFunctor(); 165 } 166 167 /** 168 * Class to facilitate dependency injection. Subclasses by test code to provide mock versions of 169 * certain AwContents dependencies. 170 */ 171 public static class DependencyFactory { createLayoutSizer()172 public AwLayoutSizer createLayoutSizer() { 173 return new AwLayoutSizer(); 174 } 175 createScrollOffsetManager( AwScrollOffsetManager.Delegate delegate, OverScroller overScroller)176 public AwScrollOffsetManager createScrollOffsetManager( 177 AwScrollOffsetManager.Delegate delegate, OverScroller overScroller) { 178 return new AwScrollOffsetManager(delegate, overScroller); 179 } 180 } 181 182 private long mNativeAwContents; 183 private final AwBrowserContext mBrowserContext; 184 private ViewGroup mContainerView; 185 private final AwLayoutChangeListener mLayoutChangeListener; 186 private final Context mContext; 187 private ContentViewCore mContentViewCore; 188 private WindowAndroid mWindowAndroid; 189 private WebContents mWebContents; 190 private NavigationController mNavigationController; 191 private final AwContentsClient mContentsClient; 192 private final AwContentViewClient mContentViewClient; 193 private WebContentsObserverAndroid mWebContentsObserver; 194 private final AwContentsClientBridge mContentsClientBridge; 195 private final AwWebContentsDelegateAdapter mWebContentsDelegate; 196 private final AwContentsIoThreadClient mIoThreadClient; 197 private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate; 198 private InternalAccessDelegate mInternalAccessAdapter; 199 private final NativeGLDelegate mNativeGLDelegate; 200 private final AwLayoutSizer mLayoutSizer; 201 private final AwZoomControls mZoomControls; 202 private final AwScrollOffsetManager mScrollOffsetManager; 203 private OverScrollGlow mOverScrollGlow; 204 // This can be accessed on any thread after construction. See AwContentsIoThreadClient. 205 private final AwSettings mSettings; 206 private final ScrollAccessibilityHelper mScrollAccessibilityHelper; 207 208 private boolean mIsPaused; 209 private boolean mIsViewVisible; 210 private boolean mIsWindowVisible; 211 private boolean mIsAttachedToWindow; 212 private Bitmap mFavicon; 213 private boolean mHasRequestedVisitedHistoryFromClient; 214 // TODO(boliu): This should be in a global context, not per webview. 215 private final double mDIPScale; 216 217 // The base background color, i.e. not accounting for any CSS body from the current page. 218 private int mBaseBackgroundColor = Color.WHITE; 219 220 // Must call nativeUpdateLastHitTestData first to update this before use. 221 private final HitTestData mPossiblyStaleHitTestData = new HitTestData(); 222 223 private final DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler; 224 225 // Bound method for suppling Picture instances to the AwContentsClient. Will be null if the 226 // picture listener API has not yet been enabled, or if it is using invalidation-only mode. 227 private Callable<Picture> mPictureListenerContentProvider; 228 229 private boolean mContainerViewFocused; 230 private boolean mWindowFocused; 231 232 // These come from the compositor and are updated synchronously (in contrast to the values in 233 // ContentViewCore, which are updated at end of every frame). 234 private float mPageScaleFactor = 1.0f; 235 private float mMinPageScaleFactor = 1.0f; 236 private float mMaxPageScaleFactor = 1.0f; 237 private float mContentWidthDip; 238 private float mContentHeightDip; 239 240 private AwAutofillClient mAwAutofillClient; 241 242 private AwPdfExporter mAwPdfExporter; 243 244 private AwViewMethods mAwViewMethods; 245 private final FullScreenTransitionsState mFullScreenTransitionsState; 246 247 // This flag indicates that ShouldOverrideUrlNavigation should be posted 248 // through the resourcethrottle. This is only used for popup windows. 249 private boolean mDeferredShouldOverrideUrlLoadingIsPendingForPopup; 250 251 // The framework may temporarily detach our container view, for example during layout if 252 // we are a child of a ListView. This may cause many toggles of View focus, which we suppress 253 // when in this state. 254 private boolean mTemporarilyDetached; 255 256 // True when this AwContents has been destroyed. 257 // Do not use directly, call isDestroyed() instead. 258 private boolean mIsDestroyed = false; 259 260 private static final class DestroyRunnable implements Runnable { 261 private final long mNativeAwContents; DestroyRunnable(long nativeAwContents)262 private DestroyRunnable(long nativeAwContents) { 263 mNativeAwContents = nativeAwContents; 264 } 265 @Override run()266 public void run() { 267 nativeDestroy(mNativeAwContents); 268 } 269 } 270 271 /** 272 * A class that stores the state needed to enter and exit fullscreen. 273 */ 274 private static class FullScreenTransitionsState { 275 private final ViewGroup mInitialContainerView; 276 private final InternalAccessDelegate mInitialInternalAccessAdapter; 277 private final AwViewMethods mInitialAwViewMethods; 278 private FullScreenView mFullScreenView; 279 FullScreenTransitionsState(ViewGroup initialContainerView, InternalAccessDelegate initialInternalAccessAdapter, AwViewMethods initialAwViewMethods)280 private FullScreenTransitionsState(ViewGroup initialContainerView, 281 InternalAccessDelegate initialInternalAccessAdapter, 282 AwViewMethods initialAwViewMethods) { 283 mInitialContainerView = initialContainerView; 284 mInitialInternalAccessAdapter = initialInternalAccessAdapter; 285 mInitialAwViewMethods = initialAwViewMethods; 286 } 287 enterFullScreen(FullScreenView fullScreenView)288 private void enterFullScreen(FullScreenView fullScreenView) { 289 mFullScreenView = fullScreenView; 290 } 291 exitFullScreen()292 private void exitFullScreen() { 293 mFullScreenView = null; 294 } 295 isFullScreen()296 private boolean isFullScreen() { 297 return mFullScreenView != null; 298 } 299 getInitialContainerView()300 private ViewGroup getInitialContainerView() { 301 return mInitialContainerView; 302 } 303 getInitialInternalAccessDelegate()304 private InternalAccessDelegate getInitialInternalAccessDelegate() { 305 return mInitialInternalAccessAdapter; 306 } 307 getInitialAwViewMethods()308 private AwViewMethods getInitialAwViewMethods() { 309 return mInitialAwViewMethods; 310 } 311 getFullScreenView()312 private FullScreenView getFullScreenView() { 313 return mFullScreenView; 314 } 315 } 316 317 // Reference to the active mNativeAwContents pointer while it is active use 318 // (ie before it is destroyed). 319 private CleanupReference mCleanupReference; 320 321 //-------------------------------------------------------------------------------------------- 322 private class IoThreadClientImpl extends AwContentsIoThreadClient { 323 // All methods are called on the IO thread. 324 325 @Override getCacheMode()326 public int getCacheMode() { 327 return mSettings.getCacheMode(); 328 } 329 330 @Override shouldInterceptRequest( AwContentsClient.ShouldInterceptRequestParams params)331 public AwWebResourceResponse shouldInterceptRequest( 332 AwContentsClient.ShouldInterceptRequestParams params) { 333 String url = params.url; 334 AwWebResourceResponse awWebResourceResponse; 335 // Return the response directly if the url is default video poster url. 336 awWebResourceResponse = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url); 337 if (awWebResourceResponse != null) return awWebResourceResponse; 338 339 awWebResourceResponse = mContentsClient.shouldInterceptRequest(params); 340 341 if (awWebResourceResponse == null) { 342 mContentsClient.getCallbackHelper().postOnLoadResource(url); 343 } 344 345 if (params.isMainFrame && awWebResourceResponse != null && 346 awWebResourceResponse.getData() == null) { 347 // In this case the intercepted URLRequest job will simulate an empty response 348 // which doesn't trigger the onReceivedError callback. For WebViewClassic 349 // compatibility we synthesize that callback. http://crbug.com/180950 350 mContentsClient.getCallbackHelper().postOnReceivedError( 351 ErrorCodeConversionHelper.ERROR_UNKNOWN, 352 null /* filled in by the glue layer */, url); 353 } 354 return awWebResourceResponse; 355 } 356 357 @Override shouldBlockContentUrls()358 public boolean shouldBlockContentUrls() { 359 return !mSettings.getAllowContentAccess(); 360 } 361 362 @Override shouldBlockFileUrls()363 public boolean shouldBlockFileUrls() { 364 return !mSettings.getAllowFileAccess(); 365 } 366 367 @Override shouldBlockNetworkLoads()368 public boolean shouldBlockNetworkLoads() { 369 return mSettings.getBlockNetworkLoads(); 370 } 371 372 @Override shouldAcceptThirdPartyCookies()373 public boolean shouldAcceptThirdPartyCookies() { 374 return mSettings.getAcceptThirdPartyCookies(); 375 } 376 377 @Override onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength)378 public void onDownloadStart(String url, String userAgent, 379 String contentDisposition, String mimeType, long contentLength) { 380 mContentsClient.getCallbackHelper().postOnDownloadStart(url, userAgent, 381 contentDisposition, mimeType, contentLength); 382 } 383 384 @Override newLoginRequest(String realm, String account, String args)385 public void newLoginRequest(String realm, String account, String args) { 386 mContentsClient.getCallbackHelper().postOnReceivedLoginRequest(realm, account, args); 387 } 388 } 389 390 //-------------------------------------------------------------------------------------------- 391 // When the navigation is for a newly created WebView (i.e. a popup), intercept the navigation 392 // here for implementing shouldOverrideUrlLoading. This is to send the shouldOverrideUrlLoading 393 // callback to the correct WebViewClient that is associated with the WebView. 394 // Otherwise, use this delegate only to post onPageStarted messages. 395 // 396 // We are not using WebContentsObserver.didStartLoading because of stale URLs, out of order 397 // onPageStarted's and double onPageStarted's. 398 // 399 private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate { 400 @Override shouldIgnoreNavigation(NavigationParams navigationParams)401 public boolean shouldIgnoreNavigation(NavigationParams navigationParams) { 402 final String url = navigationParams.url; 403 boolean ignoreNavigation = false; 404 if (mDeferredShouldOverrideUrlLoadingIsPendingForPopup) { 405 mDeferredShouldOverrideUrlLoadingIsPendingForPopup = false; 406 // If this is used for all navigations in future, cases for application initiated 407 // load, redirect and backforward should also be filtered out. 408 if (!navigationParams.isPost) { 409 ignoreNavigation = mContentsClient.shouldOverrideUrlLoading(url); 410 } 411 } 412 // The shouldOverrideUrlLoading call might have resulted in posting messages to the 413 // UI thread. Using sendMessage here (instead of calling onPageStarted directly) 414 // will allow those to run in order. 415 if (!ignoreNavigation) { 416 mContentsClient.getCallbackHelper().postOnPageStarted(url); 417 } 418 return ignoreNavigation; 419 } 420 } 421 422 //-------------------------------------------------------------------------------------------- 423 private class AwLayoutSizerDelegate implements AwLayoutSizer.Delegate { 424 @Override requestLayout()425 public void requestLayout() { 426 mContainerView.requestLayout(); 427 } 428 429 @Override setMeasuredDimension(int measuredWidth, int measuredHeight)430 public void setMeasuredDimension(int measuredWidth, int measuredHeight) { 431 mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight); 432 } 433 434 @Override isLayoutParamsHeightWrapContent()435 public boolean isLayoutParamsHeightWrapContent() { 436 return mContainerView.getLayoutParams() != null && 437 mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT; 438 } 439 440 @Override setForceZeroLayoutHeight(boolean forceZeroHeight)441 public void setForceZeroLayoutHeight(boolean forceZeroHeight) { 442 getSettings().setForceZeroLayoutHeight(forceZeroHeight); 443 } 444 } 445 446 //-------------------------------------------------------------------------------------------- 447 private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate { 448 @Override overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, boolean isTouchEvent)449 public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, 450 int scrollRangeX, int scrollRangeY, boolean isTouchEvent) { 451 mInternalAccessAdapter.overScrollBy(deltaX, deltaY, scrollX, scrollY, 452 scrollRangeX, scrollRangeY, 0, 0, isTouchEvent); 453 } 454 455 @Override scrollContainerViewTo(int x, int y)456 public void scrollContainerViewTo(int x, int y) { 457 mInternalAccessAdapter.super_scrollTo(x, y); 458 } 459 460 @Override scrollNativeTo(int x, int y)461 public void scrollNativeTo(int x, int y) { 462 if (!isDestroyed()) nativeScrollTo(mNativeAwContents, x, y); 463 } 464 465 @Override getContainerViewScrollX()466 public int getContainerViewScrollX() { 467 return mContainerView.getScrollX(); 468 } 469 470 @Override getContainerViewScrollY()471 public int getContainerViewScrollY() { 472 return mContainerView.getScrollY(); 473 } 474 475 @Override invalidate()476 public void invalidate() { 477 mContainerView.invalidate(); 478 } 479 } 480 481 //-------------------------------------------------------------------------------------------- 482 private class AwGestureStateListener extends GestureStateListener { 483 @Override onPinchStarted()484 public void onPinchStarted() { 485 // While it's possible to re-layout the view during a pinch gesture, the effect is very 486 // janky (especially that the page scale update notification comes from the renderer 487 // main thread, not from the impl thread, so it's usually out of sync with what's on 488 // screen). It's also quite expensive to do a re-layout, so we simply postpone 489 // re-layout for the duration of the gesture. This is compatible with what 490 // WebViewClassic does. 491 mLayoutSizer.freezeLayoutRequests(); 492 } 493 494 @Override onPinchEnded()495 public void onPinchEnded() { 496 mLayoutSizer.unfreezeLayoutRequests(); 497 } 498 499 @Override onFlingCancelGesture()500 public void onFlingCancelGesture() { 501 mScrollOffsetManager.onFlingCancelGesture(); 502 } 503 504 @Override onUnhandledFlingStartEvent(int velocityX, int velocityY)505 public void onUnhandledFlingStartEvent(int velocityX, int velocityY) { 506 mScrollOffsetManager.onUnhandledFlingStartEvent(velocityX, velocityY); 507 } 508 509 @Override onScrollUpdateGestureConsumed()510 public void onScrollUpdateGestureConsumed() { 511 mScrollAccessibilityHelper.postViewScrolledAccessibilityEventCallback(); 512 } 513 } 514 515 //-------------------------------------------------------------------------------------------- 516 private class AwComponentCallbacks implements ComponentCallbacks2 { 517 @Override onTrimMemory(final int level)518 public void onTrimMemory(final int level) { 519 if (isDestroyed()) return; 520 boolean visibleRectEmpty = getGlobalVisibleRect().isEmpty(); 521 final boolean visible = mIsViewVisible && mIsWindowVisible && !visibleRectEmpty; 522 nativeTrimMemory(mNativeAwContents, level, visible); 523 } 524 525 @Override onLowMemory()526 public void onLowMemory() {} 527 528 @Override onConfigurationChanged(Configuration configuration)529 public void onConfigurationChanged(Configuration configuration) {} 530 }; 531 532 //-------------------------------------------------------------------------------------------- 533 private class AwLayoutChangeListener implements View.OnLayoutChangeListener { 534 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)535 public void onLayoutChange(View v, int left, int top, int right, int bottom, 536 int oldLeft, int oldTop, int oldRight, int oldBottom) { 537 assert v == mContainerView; 538 mLayoutSizer.onLayoutChange(); 539 } 540 } 541 542 /** 543 * @param browserContext the browsing context to associate this view contents with. 544 * @param containerView the view-hierarchy item this object will be bound to. 545 * @param context the context to use, usually containerView.getContext(). 546 * @param internalAccessAdapter to access private methods on containerView. 547 * @param nativeGLDelegate to access the GL functor provided by the WebView. 548 * @param contentsClient will receive API callbacks from this WebView Contents. 549 * @param awSettings AwSettings instance used to configure the AwContents. 550 * 551 * This constructor uses the default view sizing policy. 552 */ AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate, AwContentsClient contentsClient, AwSettings awSettings)553 public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, 554 InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate, 555 AwContentsClient contentsClient, AwSettings awSettings) { 556 this(browserContext, containerView, context, internalAccessAdapter, nativeGLDelegate, 557 contentsClient, awSettings, new DependencyFactory()); 558 } 559 560 /** 561 * @param dependencyFactory an instance of the DependencyFactory used to provide instances of 562 * classes that this class depends on. 563 * 564 * This version of the constructor is used in test code to inject test versions of the above 565 * documented classes. 566 */ AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate, AwContentsClient contentsClient, AwSettings settings, DependencyFactory dependencyFactory)567 public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, 568 InternalAccessDelegate internalAccessAdapter, NativeGLDelegate nativeGLDelegate, 569 AwContentsClient contentsClient, AwSettings settings, 570 DependencyFactory dependencyFactory) { 571 mBrowserContext = browserContext; 572 mContainerView = containerView; 573 mContext = context; 574 mInternalAccessAdapter = internalAccessAdapter; 575 mNativeGLDelegate = nativeGLDelegate; 576 mContentsClient = contentsClient; 577 mAwViewMethods = new AwViewMethodsImpl(); 578 mFullScreenTransitionsState = new FullScreenTransitionsState( 579 mContainerView, mInternalAccessAdapter, mAwViewMethods); 580 mContentViewClient = new AwContentViewClient(contentsClient, settings, this, mContext); 581 mLayoutSizer = dependencyFactory.createLayoutSizer(); 582 mSettings = settings; 583 mDIPScale = DeviceDisplayInfo.create(mContext).getDIPScale(); 584 mLayoutSizer.setDelegate(new AwLayoutSizerDelegate()); 585 mLayoutSizer.setDIPScale(mDIPScale); 586 mWebContentsDelegate = new AwWebContentsDelegateAdapter( 587 contentsClient, mContainerView, mContext); 588 mContentsClientBridge = new AwContentsClientBridge(contentsClient, 589 mBrowserContext.getKeyStore(), AwContentsStatics.getClientCertLookupTable()); 590 mZoomControls = new AwZoomControls(this); 591 mIoThreadClient = new IoThreadClientImpl(); 592 mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl(); 593 594 AwSettings.ZoomSupportChangeListener zoomListener = 595 new AwSettings.ZoomSupportChangeListener() { 596 @Override 597 public void onGestureZoomSupportChanged( 598 boolean supportsDoubleTapZoom, boolean supportsMultiTouchZoom) { 599 if (isDestroyed()) return; 600 mContentViewCore.updateDoubleTapSupport(supportsDoubleTapZoom); 601 mContentViewCore.updateMultiTouchZoomSupport(supportsMultiTouchZoom); 602 } 603 604 }; 605 mSettings.setZoomListener(zoomListener); 606 mDefaultVideoPosterRequestHandler = new DefaultVideoPosterRequestHandler(mContentsClient); 607 mSettings.setDefaultVideoPosterURL( 608 mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL()); 609 mSettings.setDIPScale(mDIPScale); 610 mScrollOffsetManager = dependencyFactory.createScrollOffsetManager( 611 new AwScrollOffsetManagerDelegate(), new OverScroller(mContext)); 612 mScrollAccessibilityHelper = new ScrollAccessibilityHelper(mContainerView); 613 614 setOverScrollMode(mContainerView.getOverScrollMode()); 615 setScrollBarStyle(mInternalAccessAdapter.super_getScrollBarStyle()); 616 mLayoutChangeListener = new AwLayoutChangeListener(); 617 mContainerView.addOnLayoutChangeListener(mLayoutChangeListener); 618 619 setNewAwContents(nativeInit(mBrowserContext)); 620 621 onContainerViewChanged(); 622 } 623 createAndInitializeContentViewCore(ViewGroup containerView, Context context, InternalAccessDelegate internalDispatcher, long nativeWebContents, GestureStateListener gestureStateListener, ContentViewClient contentViewClient, ContentViewCore.ZoomControlsDelegate zoomControlsDelegate, WindowAndroid windowAndroid)624 private static ContentViewCore createAndInitializeContentViewCore(ViewGroup containerView, 625 Context context, InternalAccessDelegate internalDispatcher, long nativeWebContents, 626 GestureStateListener gestureStateListener, 627 ContentViewClient contentViewClient, 628 ContentViewCore.ZoomControlsDelegate zoomControlsDelegate, 629 WindowAndroid windowAndroid) { 630 ContentViewCore contentViewCore = new ContentViewCore(context); 631 contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents, 632 windowAndroid); 633 contentViewCore.addGestureStateListener(gestureStateListener); 634 contentViewCore.setContentViewClient(contentViewClient); 635 contentViewCore.setZoomControlsDelegate(zoomControlsDelegate); 636 return contentViewCore; 637 } 638 isFullScreen()639 boolean isFullScreen() { 640 return mFullScreenTransitionsState.isFullScreen(); 641 } 642 643 /** 644 * Transitions this {@link AwContents} to fullscreen mode and returns the 645 * {@link View} where the contents will be drawn while in fullscreen, or null 646 * if this AwContents has already been destroyed. 647 */ enterFullScreen()648 View enterFullScreen() { 649 assert !isFullScreen(); 650 if (isDestroyed()) return null; 651 652 // Detach to tear down the GL functor if this is still associated with the old 653 // container view. It will be recreated during the next call to onDraw attached to 654 // the new container view. 655 onDetachedFromWindow(); 656 657 // In fullscreen mode FullScreenView owns the AwViewMethodsImpl and AwContents 658 // a NullAwViewMethods. 659 FullScreenView fullScreenView = new FullScreenView(mContext, mAwViewMethods); 660 mFullScreenTransitionsState.enterFullScreen(fullScreenView); 661 mAwViewMethods = new NullAwViewMethods(this, mInternalAccessAdapter, mContainerView); 662 mContainerView.removeOnLayoutChangeListener(mLayoutChangeListener); 663 fullScreenView.addOnLayoutChangeListener(mLayoutChangeListener); 664 665 // Associate this AwContents with the FullScreenView. 666 setInternalAccessAdapter(fullScreenView.getInternalAccessAdapter()); 667 setContainerView(fullScreenView); 668 669 return fullScreenView; 670 } 671 672 /** 673 * Returns this {@link AwContents} to embedded mode, where the {@link AwContents} are drawn 674 * in the WebView. 675 */ exitFullScreen()676 void exitFullScreen() { 677 if (!isFullScreen() || isDestroyed()) 678 // exitFullScreen() can be called without a prior call to enterFullScreen() if a 679 // "misbehave" app overrides onShowCustomView but does not add the custom view to 680 // the window. Exiting avoids a crash. 681 return; 682 683 // Detach to tear down the GL functor if this is still associated with the old 684 // container view. It will be recreated during the next call to onDraw attached to 685 // the new container view. 686 // NOTE: we cannot use mAwViewMethods here because its type is NullAwViewMethods. 687 AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods(); 688 awViewMethodsImpl.onDetachedFromWindow(); 689 690 // Swap the view delegates. In embedded mode the FullScreenView owns a 691 // NullAwViewMethods and AwContents the AwViewMethodsImpl. 692 FullScreenView fullscreenView = mFullScreenTransitionsState.getFullScreenView(); 693 fullscreenView.setAwViewMethods(new NullAwViewMethods( 694 this, fullscreenView.getInternalAccessAdapter(), fullscreenView)); 695 mAwViewMethods = awViewMethodsImpl; 696 ViewGroup initialContainerView = mFullScreenTransitionsState.getInitialContainerView(); 697 initialContainerView.addOnLayoutChangeListener(mLayoutChangeListener); 698 fullscreenView.removeOnLayoutChangeListener(mLayoutChangeListener); 699 700 // Re-associate this AwContents with the WebView. 701 setInternalAccessAdapter(mFullScreenTransitionsState.getInitialInternalAccessDelegate()); 702 setContainerView(initialContainerView); 703 704 mFullScreenTransitionsState.exitFullScreen(); 705 } 706 setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter)707 private void setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter) { 708 mInternalAccessAdapter = internalAccessAdapter; 709 mContentViewCore.setContainerViewInternals(mInternalAccessAdapter); 710 } 711 setContainerView(ViewGroup newContainerView)712 private void setContainerView(ViewGroup newContainerView) { 713 mContainerView = newContainerView; 714 mContentViewCore.setContainerView(mContainerView); 715 if (mAwPdfExporter != null) { 716 mAwPdfExporter.setContainerView(mContainerView); 717 } 718 mWebContentsDelegate.setContainerView(mContainerView); 719 720 onContainerViewChanged(); 721 } 722 723 /** 724 * Reconciles the state of this AwContents object with the state of the new container view. 725 */ onContainerViewChanged()726 private void onContainerViewChanged() { 727 // NOTE: mAwViewMethods is used by the old container view, the WebView, so it might refer 728 // to a NullAwViewMethods when in fullscreen. To ensure that the state is reconciled with 729 // the new container view correctly, we bypass mAwViewMethods and use the real 730 // implementation directly. 731 AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods(); 732 awViewMethodsImpl.onVisibilityChanged(mContainerView, mContainerView.getVisibility()); 733 awViewMethodsImpl.onWindowVisibilityChanged(mContainerView.getWindowVisibility()); 734 735 // We should stop running WebView tests in JellyBean devices, see crbug/161864. 736 // Until then we skip calling isAttachedToWindow() as it has only been introduced in K. 737 if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT 738 || mContainerView.isAttachedToWindow()) { 739 awViewMethodsImpl.onAttachedToWindow(); 740 } else { 741 awViewMethodsImpl.onDetachedFromWindow(); 742 } 743 awViewMethodsImpl.onSizeChanged( 744 mContainerView.getWidth(), mContainerView.getHeight(), 0, 0); 745 awViewMethodsImpl.onWindowFocusChanged(mContainerView.hasWindowFocus()); 746 awViewMethodsImpl.onFocusChanged(mContainerView.hasFocus(), 0, null); 747 mContainerView.requestLayout(); 748 } 749 750 /* Common initialization routine for adopting a native AwContents instance into this 751 * java instance. 752 * 753 * TAKE CARE! This method can get called multiple times per java instance. Code accordingly. 754 * ^^^^^^^^^ See the native class declaration for more details on relative object lifetimes. 755 */ setNewAwContents(long newAwContentsPtr)756 private void setNewAwContents(long newAwContentsPtr) { 757 if (mNativeAwContents != 0) { 758 destroyNatives(); 759 mContentViewCore = null; 760 mWebContents = null; 761 mNavigationController = null; 762 } 763 764 assert mNativeAwContents == 0 && mCleanupReference == null && mContentViewCore == null; 765 766 mNativeAwContents = newAwContentsPtr; 767 // TODO(joth): when the native and java counterparts of AwBrowserContext are hooked up to 768 // each other, we should update |mBrowserContext| according to the newly received native 769 // WebContent's browser context. 770 771 // The native side object has been bound to this java instance, so now is the time to 772 // bind all the native->java relationships. 773 mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents)); 774 775 long nativeWebContents = nativeGetWebContents(mNativeAwContents); 776 777 mWindowAndroid = mContext instanceof Activity ? 778 new ActivityWindowAndroid((Activity) mContext) : 779 new WindowAndroid(mContext.getApplicationContext()); 780 mContentViewCore = createAndInitializeContentViewCore( 781 mContainerView, mContext, mInternalAccessAdapter, nativeWebContents, 782 new AwGestureStateListener(), mContentViewClient, mZoomControls, mWindowAndroid); 783 nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge, 784 mIoThreadClient, mInterceptNavigationDelegate); 785 mWebContents = mContentViewCore.getWebContents(); 786 mNavigationController = mWebContents.getNavigationController(); 787 installWebContentsObserver(); 788 mSettings.setWebContents(nativeWebContents); 789 nativeSetDipScale(mNativeAwContents, (float) mDIPScale); 790 mContentViewCore.onShow(); 791 } 792 installWebContentsObserver()793 private void installWebContentsObserver() { 794 if (mWebContentsObserver != null) { 795 mWebContentsObserver.detachFromWebContents(); 796 } 797 mWebContentsObserver = new AwWebContentsObserver(mWebContents, mContentsClient); 798 } 799 800 /** 801 * Called on the "source" AwContents that is opening the popup window to 802 * provide the AwContents to host the pop up content. 803 */ supplyContentsForPopup(AwContents newContents)804 public void supplyContentsForPopup(AwContents newContents) { 805 long popupNativeAwContents = nativeReleasePopupAwContents(mNativeAwContents); 806 if (popupNativeAwContents == 0) { 807 Log.w(TAG, "Popup WebView bind failed: no pending content."); 808 if (newContents != null) newContents.destroy(); 809 return; 810 } 811 if (newContents == null) { 812 nativeDestroy(popupNativeAwContents); 813 return; 814 } 815 816 newContents.receivePopupContents(popupNativeAwContents); 817 } 818 819 // Recap: supplyContentsForPopup() is called on the parent window's content, this method is 820 // called on the popup window's content. receivePopupContents(long popupNativeAwContents)821 private void receivePopupContents(long popupNativeAwContents) { 822 mDeferredShouldOverrideUrlLoadingIsPendingForPopup = true; 823 // Save existing view state. 824 final boolean wasAttached = mIsAttachedToWindow; 825 final boolean wasViewVisible = mIsViewVisible; 826 final boolean wasWindowVisible = mIsWindowVisible; 827 final boolean wasPaused = mIsPaused; 828 final boolean wasFocused = mContainerViewFocused; 829 final boolean wasWindowFocused = mWindowFocused; 830 831 // Properly clean up existing mContentViewCore and mNativeAwContents. 832 if (wasFocused) onFocusChanged(false, 0, null); 833 if (wasWindowFocused) onWindowFocusChanged(false); 834 if (wasViewVisible) setViewVisibilityInternal(false); 835 if (wasWindowVisible) setWindowVisibilityInternal(false); 836 if (wasAttached) onDetachedFromWindow(); 837 if (!wasPaused) onPause(); 838 839 // Save injected JavaScript interfaces. 840 Map<String, Pair<Object, Class>> javascriptInterfaces = 841 new HashMap<String, Pair<Object, Class>>(); 842 if (mContentViewCore != null) { 843 javascriptInterfaces.putAll(mContentViewCore.getJavascriptInterfaces()); 844 } 845 846 setNewAwContents(popupNativeAwContents); 847 848 // Finally refresh all view state for mContentViewCore and mNativeAwContents. 849 if (!wasPaused) onResume(); 850 if (wasAttached) { 851 onAttachedToWindow(); 852 postInvalidateOnAnimation(); 853 } 854 onSizeChanged(mContainerView.getWidth(), mContainerView.getHeight(), 0, 0); 855 if (wasWindowVisible) setWindowVisibilityInternal(true); 856 if (wasViewVisible) setViewVisibilityInternal(true); 857 if (wasWindowFocused) onWindowFocusChanged(wasWindowFocused); 858 if (wasFocused) onFocusChanged(true, 0, null); 859 860 // Restore injected JavaScript interfaces. 861 for (Map.Entry<String, Pair<Object, Class>> entry : javascriptInterfaces.entrySet()) { 862 @SuppressWarnings("unchecked") 863 Class<? extends Annotation> requiredAnnotation = entry.getValue().second; 864 mContentViewCore.addPossiblyUnsafeJavascriptInterface( 865 entry.getValue().first, 866 entry.getKey(), 867 requiredAnnotation); 868 } 869 } 870 871 /** 872 * Destroys this object and deletes its native counterpart. 873 */ destroy()874 public void destroy() { 875 mIsDestroyed = true; 876 destroyNatives(); 877 } 878 879 /** 880 * Deletes the native counterpart of this object. 881 */ destroyNatives()882 private void destroyNatives() { 883 if (mCleanupReference != null) { 884 assert mNativeAwContents != 0; 885 // If we are attached, we have to call native detach to clean up 886 // hardware resources. 887 if (mIsAttachedToWindow) { 888 nativeOnDetachedFromWindow(mNativeAwContents); 889 } 890 891 mWebContentsObserver.detachFromWebContents(); 892 mWebContentsObserver = null; 893 mContentViewCore.destroy(); 894 mContentViewCore = null; 895 mNativeAwContents = 0; 896 mWebContents = null; 897 mNavigationController = null; 898 899 mCleanupReference.cleanupNow(); 900 mCleanupReference = null; 901 } 902 903 assert mContentViewCore == null; 904 assert mWebContents == null; 905 assert mNavigationController == null; 906 assert mNativeAwContents == 0; 907 } 908 isDestroyed()909 private boolean isDestroyed() { 910 if (mIsDestroyed) { 911 assert mContentViewCore == null; 912 assert mWebContents == null; 913 assert mNavigationController == null; 914 assert mNativeAwContents == 0; 915 } else { 916 assert mContentViewCore != null; 917 assert mWebContents != null; 918 assert mNavigationController != null; 919 assert mNativeAwContents != 0; 920 } 921 return mIsDestroyed; 922 } 923 924 @VisibleForTesting getContentViewCore()925 public ContentViewCore getContentViewCore() { 926 return mContentViewCore; 927 } 928 929 @VisibleForTesting getWebContents()930 public WebContents getWebContents() { 931 return mWebContents; 932 } 933 934 @VisibleForTesting getNavigationController()935 public NavigationController getNavigationController() { 936 return mNavigationController; 937 } 938 939 // Can be called from any thread. getSettings()940 public AwSettings getSettings() { 941 return mSettings; 942 } 943 getPdfExporter()944 public AwPdfExporter getPdfExporter() { 945 if (isDestroyed()) return null; 946 if (mAwPdfExporter == null) { 947 mAwPdfExporter = new AwPdfExporter(mContainerView); 948 nativeCreatePdfExporter(mNativeAwContents, mAwPdfExporter); 949 } 950 return mAwPdfExporter; 951 } 952 setAwDrawSWFunctionTable(long functionTablePointer)953 public static void setAwDrawSWFunctionTable(long functionTablePointer) { 954 nativeSetAwDrawSWFunctionTable(functionTablePointer); 955 } 956 setAwDrawGLFunctionTable(long functionTablePointer)957 public static void setAwDrawGLFunctionTable(long functionTablePointer) { 958 nativeSetAwDrawGLFunctionTable(functionTablePointer); 959 } 960 getAwDrawGLFunction()961 public static long getAwDrawGLFunction() { 962 return nativeGetAwDrawGLFunction(); 963 } 964 setShouldDownloadFavicons()965 public static void setShouldDownloadFavicons() { 966 nativeSetShouldDownloadFavicons(); 967 } 968 969 /** 970 * Disables contents of JS-to-Java bridge objects to be inspectable using 971 * Object.keys() method and "for .. in" loops. This is intended for applications 972 * targeting earlier Android releases where this was not possible, and we want 973 * to ensure backwards compatible behavior. 974 */ disableJavascriptInterfacesInspection()975 public void disableJavascriptInterfacesInspection() { 976 if (!isDestroyed()) mContentViewCore.setAllowJavascriptInterfacesInspection(false); 977 } 978 979 /** 980 * Intended for test code. 981 * @return the number of native instances of this class. 982 */ 983 @VisibleForTesting getNativeInstanceCount()984 public static int getNativeInstanceCount() { 985 return nativeGetNativeInstanceCount(); 986 } 987 getAwDrawGLViewContext()988 public long getAwDrawGLViewContext() { 989 // Only called during early construction, so client should not have had a chance to 990 // call destroy yet. 991 assert !isDestroyed(); 992 993 // Using the native pointer as the returned viewContext. This is matched by the 994 // reinterpret_cast back to BrowserViewRenderer pointer in the native DrawGLFunction. 995 return nativeGetAwDrawGLViewContext(mNativeAwContents); 996 } 997 998 // This is only to avoid heap allocations inside getGlobalVisibleRect. It should treated 999 // as a local variable in the function and not used anywhere else. 1000 private static final Rect sLocalGlobalVisibleRect = new Rect(); 1001 getGlobalVisibleRect()1002 private Rect getGlobalVisibleRect() { 1003 if (!mContainerView.getGlobalVisibleRect(sLocalGlobalVisibleRect)) { 1004 sLocalGlobalVisibleRect.setEmpty(); 1005 } 1006 return sLocalGlobalVisibleRect; 1007 } 1008 1009 //-------------------------------------------------------------------------------------------- 1010 // WebView[Provider] method implementations (where not provided by ContentViewCore) 1011 //-------------------------------------------------------------------------------------------- 1012 onDraw(Canvas canvas)1013 public void onDraw(Canvas canvas) { 1014 mAwViewMethods.onDraw(canvas); 1015 } 1016 onMeasure(int widthMeasureSpec, int heightMeasureSpec)1017 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1018 mAwViewMethods.onMeasure(widthMeasureSpec, heightMeasureSpec); 1019 } 1020 getContentHeightCss()1021 public int getContentHeightCss() { 1022 return (int) Math.ceil(mContentHeightDip); 1023 } 1024 getContentWidthCss()1025 public int getContentWidthCss() { 1026 return (int) Math.ceil(mContentWidthDip); 1027 } 1028 capturePicture()1029 public Picture capturePicture() { 1030 if (isDestroyed()) return null; 1031 return new AwPicture(nativeCapturePicture(mNativeAwContents, 1032 mScrollOffsetManager.computeHorizontalScrollRange(), 1033 mScrollOffsetManager.computeVerticalScrollRange())); 1034 } 1035 clearView()1036 public void clearView() { 1037 if (!isDestroyed()) nativeClearView(mNativeAwContents); 1038 } 1039 1040 /** 1041 * Enable the onNewPicture callback. 1042 * @param enabled Flag to enable the callback. 1043 * @param invalidationOnly Flag to call back only on invalidation without providing a picture. 1044 */ enableOnNewPicture(boolean enabled, boolean invalidationOnly)1045 public void enableOnNewPicture(boolean enabled, boolean invalidationOnly) { 1046 if (isDestroyed()) return; 1047 if (invalidationOnly) { 1048 mPictureListenerContentProvider = null; 1049 } else if (enabled && mPictureListenerContentProvider == null) { 1050 mPictureListenerContentProvider = new Callable<Picture>() { 1051 @Override 1052 public Picture call() { 1053 return capturePicture(); 1054 } 1055 }; 1056 } 1057 nativeEnableOnNewPicture(mNativeAwContents, enabled); 1058 } 1059 findAllAsync(String searchString)1060 public void findAllAsync(String searchString) { 1061 if (!isDestroyed()) nativeFindAllAsync(mNativeAwContents, searchString); 1062 } 1063 findNext(boolean forward)1064 public void findNext(boolean forward) { 1065 if (!isDestroyed()) nativeFindNext(mNativeAwContents, forward); 1066 } 1067 clearMatches()1068 public void clearMatches() { 1069 if (!isDestroyed()) nativeClearMatches(mNativeAwContents); 1070 } 1071 1072 /** 1073 * @return load progress of the WebContents. 1074 */ getMostRecentProgress()1075 public int getMostRecentProgress() { 1076 // WebContentsDelegateAndroid conveniently caches the most recent notified value for us. 1077 return mWebContentsDelegate.getMostRecentProgress(); 1078 } 1079 getFavicon()1080 public Bitmap getFavicon() { 1081 return mFavicon; 1082 } 1083 requestVisitedHistoryFromClient()1084 private void requestVisitedHistoryFromClient() { 1085 ValueCallback<String[]> callback = new ValueCallback<String[]>() { 1086 @Override 1087 public void onReceiveValue(final String[] value) { 1088 ThreadUtils.runOnUiThread(new Runnable() { 1089 @Override 1090 public void run() { 1091 if (!isDestroyed()) nativeAddVisitedLinks(mNativeAwContents, value); 1092 } 1093 }); 1094 } 1095 }; 1096 mContentsClient.getVisitedHistory(callback); 1097 } 1098 1099 /** 1100 * Load url without fixing up the url string. Consumers of ContentView are responsible for 1101 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left 1102 * off during user input). 1103 * 1104 * @param params Parameters for this load. 1105 */ loadUrl(LoadUrlParams params)1106 public void loadUrl(LoadUrlParams params) { 1107 if (isDestroyed()) return; 1108 1109 if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA && 1110 !params.isBaseUrlDataScheme()) { 1111 // This allows data URLs with a non-data base URL access to file:///android_asset/ and 1112 // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also 1113 // allow access to file:// URLs (subject to OS level permission checks). 1114 params.setCanLoadLocalResources(true); 1115 } 1116 1117 // If we are reloading the same url, then set transition type as reload. 1118 if (params.getUrl() != null && 1119 params.getUrl().equals(mWebContents.getUrl()) && 1120 params.getTransitionType() == PageTransitionTypes.PAGE_TRANSITION_LINK) { 1121 params.setTransitionType(PageTransitionTypes.PAGE_TRANSITION_RELOAD); 1122 } 1123 params.setTransitionType( 1124 params.getTransitionType() | PageTransitionTypes.PAGE_TRANSITION_FROM_API); 1125 1126 // For WebView, always use the user agent override, which is set 1127 // every time the user agent in AwSettings is modified. 1128 params.setOverrideUserAgent(LoadUrlParams.UA_OVERRIDE_TRUE); 1129 1130 1131 // We don't pass extra headers to the content layer, as WebViewClassic 1132 // was adding them in a very narrow set of conditions. See http://crbug.com/306873 1133 // However, if the embedder is attempting to inject a Referer header for their 1134 // loadUrl call, then we set that separately and remove it from the extra headers map/ 1135 final String referer = "referer"; 1136 Map<String, String> extraHeaders = params.getExtraHeaders(); 1137 if (extraHeaders != null) { 1138 for (String header : extraHeaders.keySet()) { 1139 if (referer.equals(header.toLowerCase(Locale.US))) { 1140 params.setReferrer(new Referrer(extraHeaders.remove(header), 1)); 1141 params.setExtraHeaders(extraHeaders); 1142 break; 1143 } 1144 } 1145 } 1146 1147 nativeSetExtraHeadersForUrl( 1148 mNativeAwContents, params.getUrl(), params.getExtraHttpRequestHeadersString()); 1149 params.setExtraHeaders(new HashMap<String, String>()); 1150 1151 mNavigationController.loadUrl(params); 1152 1153 // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit. 1154 // Chromium does not use this use code path and the best emulation of this behavior to call 1155 // request visited links once on the first URL load of the WebView. 1156 if (!mHasRequestedVisitedHistoryFromClient) { 1157 mHasRequestedVisitedHistoryFromClient = true; 1158 requestVisitedHistoryFromClient(); 1159 } 1160 1161 if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA && 1162 params.getBaseUrl() != null) { 1163 // Data loads with a base url will be resolved in Blink, and not cause an onPageStarted 1164 // event to be sent. Sending the callback directly from here. 1165 mContentsClient.getCallbackHelper().postOnPageStarted(params.getBaseUrl()); 1166 } 1167 } 1168 1169 /** 1170 * Get the URL of the current page. 1171 * 1172 * @return The URL of the current page or null if it's empty. 1173 */ getUrl()1174 public String getUrl() { 1175 if (isDestroyed()) return null; 1176 String url = mWebContents.getUrl(); 1177 if (url == null || url.trim().isEmpty()) return null; 1178 return url; 1179 } 1180 requestFocus()1181 public void requestFocus() { 1182 mAwViewMethods.requestFocus(); 1183 } 1184 setBackgroundColor(int color)1185 public void setBackgroundColor(int color) { 1186 mBaseBackgroundColor = color; 1187 if (!isDestroyed()) nativeSetBackgroundColor(mNativeAwContents, color); 1188 } 1189 1190 /** 1191 * @see android.view.View#setLayerType() 1192 */ setLayerType(int layerType, Paint paint)1193 public void setLayerType(int layerType, Paint paint) { 1194 mAwViewMethods.setLayerType(layerType, paint); 1195 } 1196 getEffectiveBackgroundColor()1197 int getEffectiveBackgroundColor() { 1198 // Do not ask the ContentViewCore for the background color, as it will always 1199 // report white prior to initial navigation or post destruction, whereas we want 1200 // to use the client supplied base value in those cases. 1201 if (isDestroyed() || !mContentsClient.isCachedRendererBackgroundColorValid()) { 1202 return mBaseBackgroundColor; 1203 } 1204 return mContentsClient.getCachedRendererBackgroundColor(); 1205 } 1206 isMultiTouchZoomSupported()1207 public boolean isMultiTouchZoomSupported() { 1208 return mSettings.supportsMultiTouchZoom(); 1209 } 1210 getZoomControlsForTest()1211 public View getZoomControlsForTest() { 1212 return mZoomControls.getZoomControlsViewForTest(); 1213 } 1214 1215 /** 1216 * @see ContentViewCore#getContentSettings() 1217 */ getContentSettings()1218 public ContentSettings getContentSettings() { 1219 return isDestroyed() ? null : mContentViewCore.getContentSettings(); 1220 } 1221 1222 /** 1223 * @see View#setOverScrollMode(int) 1224 */ setOverScrollMode(int mode)1225 public void setOverScrollMode(int mode) { 1226 if (mode != View.OVER_SCROLL_NEVER) { 1227 mOverScrollGlow = new OverScrollGlow(mContext, mContainerView); 1228 } else { 1229 mOverScrollGlow = null; 1230 } 1231 } 1232 1233 // TODO(mkosiba): In WebViewClassic these appear in some of the scroll extent calculation 1234 // methods but toggling them has no visiual effect on the content (in other words the scrolling 1235 // code behaves as if the scrollbar-related padding is in place but the onDraw code doesn't 1236 // take that into consideration). 1237 // http://crbug.com/269032 1238 private boolean mOverlayHorizontalScrollbar = true; 1239 private boolean mOverlayVerticalScrollbar = false; 1240 1241 /** 1242 * @see View#setScrollBarStyle(int) 1243 */ setScrollBarStyle(int style)1244 public void setScrollBarStyle(int style) { 1245 if (style == View.SCROLLBARS_INSIDE_OVERLAY 1246 || style == View.SCROLLBARS_OUTSIDE_OVERLAY) { 1247 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true; 1248 } else { 1249 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false; 1250 } 1251 } 1252 1253 /** 1254 * @see View#setHorizontalScrollbarOverlay(boolean) 1255 */ setHorizontalScrollbarOverlay(boolean overlay)1256 public void setHorizontalScrollbarOverlay(boolean overlay) { 1257 mOverlayHorizontalScrollbar = overlay; 1258 } 1259 1260 /** 1261 * @see View#setVerticalScrollbarOverlay(boolean) 1262 */ setVerticalScrollbarOverlay(boolean overlay)1263 public void setVerticalScrollbarOverlay(boolean overlay) { 1264 mOverlayVerticalScrollbar = overlay; 1265 } 1266 1267 /** 1268 * @see View#overlayHorizontalScrollbar() 1269 */ overlayHorizontalScrollbar()1270 public boolean overlayHorizontalScrollbar() { 1271 return mOverlayHorizontalScrollbar; 1272 } 1273 1274 /** 1275 * @see View#overlayVerticalScrollbar() 1276 */ overlayVerticalScrollbar()1277 public boolean overlayVerticalScrollbar() { 1278 return mOverlayVerticalScrollbar; 1279 } 1280 1281 /** 1282 * Called by the embedder when the scroll offset of the containing view has changed. 1283 * @see View#onScrollChanged(int,int) 1284 */ onContainerViewScrollChanged(int l, int t, int oldl, int oldt)1285 public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) { 1286 // A side-effect of View.onScrollChanged is that the scroll accessibility event being sent 1287 // by the base class implementation. This is completely hidden from the base classes and 1288 // cannot be prevented, which is why we need the code below. 1289 mScrollAccessibilityHelper.removePostedViewScrolledAccessibilityEventCallback(); 1290 mScrollOffsetManager.onContainerViewScrollChanged(l, t); 1291 } 1292 1293 /** 1294 * Called by the embedder when the containing view is to be scrolled or overscrolled. 1295 * @see View#onOverScrolled(int,int,int,int) 1296 */ onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)1297 public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, 1298 boolean clampedY) { 1299 int oldX = mContainerView.getScrollX(); 1300 int oldY = mContainerView.getScrollY(); 1301 1302 mScrollOffsetManager.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); 1303 1304 if (mOverScrollGlow != null) { 1305 mOverScrollGlow.pullGlow(mContainerView.getScrollX(), mContainerView.getScrollY(), 1306 oldX, oldY, 1307 mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), 1308 mScrollOffsetManager.computeMaximumVerticalScrollOffset()); 1309 } 1310 } 1311 1312 /** 1313 * @see android.webkit.WebView#requestChildRectangleOnScreen(View, Rect, boolean) 1314 */ requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)1315 public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) { 1316 return mScrollOffsetManager.requestChildRectangleOnScreen( 1317 child.getLeft() - child.getScrollX(), child.getTop() - child.getScrollY(), 1318 rect, immediate); 1319 } 1320 1321 /** 1322 * @see View.computeScroll() 1323 */ computeScroll()1324 public void computeScroll() { 1325 mScrollOffsetManager.computeScrollAndAbsorbGlow(mOverScrollGlow); 1326 } 1327 1328 /** 1329 * @see View#computeHorizontalScrollRange() 1330 */ computeHorizontalScrollRange()1331 public int computeHorizontalScrollRange() { 1332 return mScrollOffsetManager.computeHorizontalScrollRange(); 1333 } 1334 1335 /** 1336 * @see View#computeHorizontalScrollOffset() 1337 */ computeHorizontalScrollOffset()1338 public int computeHorizontalScrollOffset() { 1339 return mScrollOffsetManager.computeHorizontalScrollOffset(); 1340 } 1341 1342 /** 1343 * @see View#computeVerticalScrollRange() 1344 */ computeVerticalScrollRange()1345 public int computeVerticalScrollRange() { 1346 return mScrollOffsetManager.computeVerticalScrollRange(); 1347 } 1348 1349 /** 1350 * @see View#computeVerticalScrollOffset() 1351 */ computeVerticalScrollOffset()1352 public int computeVerticalScrollOffset() { 1353 return mScrollOffsetManager.computeVerticalScrollOffset(); 1354 } 1355 1356 /** 1357 * @see View#computeVerticalScrollExtent() 1358 */ computeVerticalScrollExtent()1359 public int computeVerticalScrollExtent() { 1360 return mScrollOffsetManager.computeVerticalScrollExtent(); 1361 } 1362 1363 /** 1364 * @see android.webkit.WebView#stopLoading() 1365 */ stopLoading()1366 public void stopLoading() { 1367 if (!isDestroyed()) mWebContents.stop(); 1368 } 1369 1370 /** 1371 * @see android.webkit.WebView#reload() 1372 */ reload()1373 public void reload() { 1374 if (!isDestroyed()) mNavigationController.reload(true); 1375 } 1376 1377 /** 1378 * @see android.webkit.WebView#canGoBack() 1379 */ canGoBack()1380 public boolean canGoBack() { 1381 return isDestroyed() ? false : mNavigationController.canGoBack(); 1382 } 1383 1384 /** 1385 * @see android.webkit.WebView#goBack() 1386 */ goBack()1387 public void goBack() { 1388 if (!isDestroyed()) mNavigationController.goBack(); 1389 } 1390 1391 /** 1392 * @see android.webkit.WebView#canGoForward() 1393 */ canGoForward()1394 public boolean canGoForward() { 1395 return isDestroyed() ? false : mNavigationController.canGoForward(); 1396 } 1397 1398 /** 1399 * @see android.webkit.WebView#goForward() 1400 */ goForward()1401 public void goForward() { 1402 if (!isDestroyed()) mNavigationController.goForward(); 1403 } 1404 1405 /** 1406 * @see android.webkit.WebView#canGoBackOrForward(int) 1407 */ canGoBackOrForward(int steps)1408 public boolean canGoBackOrForward(int steps) { 1409 return isDestroyed() ? false : mNavigationController.canGoToOffset(steps); 1410 } 1411 1412 /** 1413 * @see android.webkit.WebView#goBackOrForward(int) 1414 */ goBackOrForward(int steps)1415 public void goBackOrForward(int steps) { 1416 if (!isDestroyed()) mNavigationController.goToOffset(steps); 1417 } 1418 1419 /** 1420 * @see android.webkit.WebView#pauseTimers() 1421 */ pauseTimers()1422 public void pauseTimers() { 1423 if (!isDestroyed()) ContentViewStatics.setWebKitSharedTimersSuspended(true); 1424 } 1425 1426 /** 1427 * @see android.webkit.WebView#resumeTimers() 1428 */ resumeTimers()1429 public void resumeTimers() { 1430 if (!isDestroyed()) ContentViewStatics.setWebKitSharedTimersSuspended(false); 1431 } 1432 1433 /** 1434 * @see android.webkit.WebView#onPause() 1435 */ onPause()1436 public void onPause() { 1437 if (mIsPaused || isDestroyed()) return; 1438 mIsPaused = true; 1439 nativeSetIsPaused(mNativeAwContents, mIsPaused); 1440 mContentViewCore.onHide(); 1441 } 1442 1443 /** 1444 * @see android.webkit.WebView#onResume() 1445 */ onResume()1446 public void onResume() { 1447 if (!mIsPaused || isDestroyed()) return; 1448 mIsPaused = false; 1449 nativeSetIsPaused(mNativeAwContents, mIsPaused); 1450 mContentViewCore.onShow(); 1451 } 1452 1453 /** 1454 * @see android.webkit.WebView#isPaused() 1455 */ isPaused()1456 public boolean isPaused() { 1457 return mIsPaused; 1458 } 1459 1460 /** 1461 * @see android.webkit.WebView#onCreateInputConnection(EditorInfo) 1462 */ onCreateInputConnection(EditorInfo outAttrs)1463 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 1464 return mAwViewMethods.onCreateInputConnection(outAttrs); 1465 } 1466 1467 /** 1468 * @see android.webkit.WebView#onKeyUp(int, KeyEvent) 1469 */ onKeyUp(int keyCode, KeyEvent event)1470 public boolean onKeyUp(int keyCode, KeyEvent event) { 1471 return mAwViewMethods.onKeyUp(keyCode, event); 1472 } 1473 1474 /** 1475 * @see android.webkit.WebView#dispatchKeyEvent(KeyEvent) 1476 */ dispatchKeyEvent(KeyEvent event)1477 public boolean dispatchKeyEvent(KeyEvent event) { 1478 return mAwViewMethods.dispatchKeyEvent(event); 1479 } 1480 1481 /** 1482 * Clears the resource cache. Note that the cache is per-application, so this will clear the 1483 * cache for all WebViews used. 1484 * 1485 * @param includeDiskFiles if false, only the RAM cache is cleared 1486 */ clearCache(boolean includeDiskFiles)1487 public void clearCache(boolean includeDiskFiles) { 1488 if (!isDestroyed()) nativeClearCache(mNativeAwContents, includeDiskFiles); 1489 } 1490 documentHasImages(Message message)1491 public void documentHasImages(Message message) { 1492 if (!isDestroyed()) nativeDocumentHasImages(mNativeAwContents, message); 1493 } 1494 saveWebArchive( final String basename, boolean autoname, final ValueCallback<String> callback)1495 public void saveWebArchive( 1496 final String basename, boolean autoname, final ValueCallback<String> callback) { 1497 if (!autoname) { 1498 saveWebArchiveInternal(basename, callback); 1499 return; 1500 } 1501 // If auto-generating the file name, handle the name generation on a background thread 1502 // as it will require I/O access for checking whether previous files existed. 1503 new AsyncTask<Void, Void, String>() { 1504 @Override 1505 protected String doInBackground(Void... params) { 1506 return generateArchiveAutoNamePath(getOriginalUrl(), basename); 1507 } 1508 1509 @Override 1510 protected void onPostExecute(String result) { 1511 saveWebArchiveInternal(result, callback); 1512 } 1513 }.execute(); 1514 } 1515 getOriginalUrl()1516 public String getOriginalUrl() { 1517 if (isDestroyed()) return null; 1518 NavigationHistory history = mNavigationController.getNavigationHistory(); 1519 int currentIndex = history.getCurrentEntryIndex(); 1520 if (currentIndex >= 0 && currentIndex < history.getEntryCount()) { 1521 return history.getEntryAtIndex(currentIndex).getOriginalUrl(); 1522 } 1523 return null; 1524 } 1525 1526 /** 1527 * @see ContentViewCore#getNavigationHistory() 1528 */ getNavigationHistory()1529 public NavigationHistory getNavigationHistory() { 1530 return isDestroyed() ? null : mNavigationController.getNavigationHistory(); 1531 } 1532 1533 /** 1534 * @see android.webkit.WebView#getTitle() 1535 */ getTitle()1536 public String getTitle() { 1537 return isDestroyed() ? null : mWebContents.getTitle(); 1538 } 1539 1540 /** 1541 * @see android.webkit.WebView#clearHistory() 1542 */ clearHistory()1543 public void clearHistory() { 1544 if (!isDestroyed()) mNavigationController.clearHistory(); 1545 } 1546 getHttpAuthUsernamePassword(String host, String realm)1547 public String[] getHttpAuthUsernamePassword(String host, String realm) { 1548 return mBrowserContext.getHttpAuthDatabase(mContext) 1549 .getHttpAuthUsernamePassword(host, realm); 1550 } 1551 setHttpAuthUsernamePassword(String host, String realm, String username, String password)1552 public void setHttpAuthUsernamePassword(String host, String realm, String username, 1553 String password) { 1554 mBrowserContext.getHttpAuthDatabase(mContext) 1555 .setHttpAuthUsernamePassword(host, realm, username, password); 1556 } 1557 1558 /** 1559 * @see android.webkit.WebView#getCertificate() 1560 */ getCertificate()1561 public SslCertificate getCertificate() { 1562 return isDestroyed() ? null 1563 : SslUtil.getCertificateFromDerBytes(nativeGetCertificate(mNativeAwContents)); 1564 } 1565 1566 /** 1567 * @see android.webkit.WebView#clearSslPreferences() 1568 */ clearSslPreferences()1569 public void clearSslPreferences() { 1570 if (!isDestroyed()) mNavigationController.clearSslPreferences(); 1571 } 1572 1573 // TODO(sgurun) remove after this rolls in. To keep internal tree happy. clearClientCertPreferences()1574 public void clearClientCertPreferences() { } 1575 1576 /** 1577 * Method to return all hit test values relevant to public WebView API. 1578 * Note that this expose more data than needed for WebView.getHitTestResult. 1579 * Unsafely returning reference to mutable internal object to avoid excessive 1580 * garbage allocation on repeated calls. 1581 */ getLastHitTestResult()1582 public HitTestData getLastHitTestResult() { 1583 if (isDestroyed()) return null; 1584 nativeUpdateLastHitTestData(mNativeAwContents); 1585 return mPossiblyStaleHitTestData; 1586 } 1587 1588 /** 1589 * @see android.webkit.WebView#requestFocusNodeHref() 1590 */ requestFocusNodeHref(Message msg)1591 public void requestFocusNodeHref(Message msg) { 1592 if (msg == null || isDestroyed()) return; 1593 1594 nativeUpdateLastHitTestData(mNativeAwContents); 1595 Bundle data = msg.getData(); 1596 1597 // In order to maintain compatibility with the old WebView's implementation, 1598 // the absolute (full) url is passed in the |url| field, not only the href attribute. 1599 // Note: HitTestData could be cleaned up at this point. See http://crbug.com/290992. 1600 data.putString("url", mPossiblyStaleHitTestData.href); 1601 data.putString("title", mPossiblyStaleHitTestData.anchorText); 1602 data.putString("src", mPossiblyStaleHitTestData.imgSrc); 1603 msg.setData(data); 1604 msg.sendToTarget(); 1605 } 1606 1607 /** 1608 * @see android.webkit.WebView#requestImageRef() 1609 */ requestImageRef(Message msg)1610 public void requestImageRef(Message msg) { 1611 if (msg == null || isDestroyed()) return; 1612 1613 nativeUpdateLastHitTestData(mNativeAwContents); 1614 Bundle data = msg.getData(); 1615 data.putString("url", mPossiblyStaleHitTestData.imgSrc); 1616 msg.setData(data); 1617 msg.sendToTarget(); 1618 } 1619 1620 @VisibleForTesting getPageScaleFactor()1621 public float getPageScaleFactor() { 1622 return mPageScaleFactor; 1623 } 1624 1625 /** 1626 * @see android.webkit.WebView#getScale() 1627 * 1628 * Please note that the scale returned is the page scale multiplied by 1629 * the screen density factor. See CTS WebViewTest.testSetInitialScale. 1630 */ getScale()1631 public float getScale() { 1632 return (float) (mPageScaleFactor * mDIPScale); 1633 } 1634 1635 /** 1636 * @see android.webkit.WebView#flingScroll(int, int) 1637 */ flingScroll(int velocityX, int velocityY)1638 public void flingScroll(int velocityX, int velocityY) { 1639 mScrollOffsetManager.flingScroll(velocityX, velocityY); 1640 } 1641 1642 /** 1643 * @see android.webkit.WebView#pageUp(boolean) 1644 */ pageUp(boolean top)1645 public boolean pageUp(boolean top) { 1646 return mScrollOffsetManager.pageUp(top); 1647 } 1648 1649 /** 1650 * @see android.webkit.WebView#pageDown(boolean) 1651 */ pageDown(boolean bottom)1652 public boolean pageDown(boolean bottom) { 1653 return mScrollOffsetManager.pageDown(bottom); 1654 } 1655 1656 /** 1657 * @see android.webkit.WebView#canZoomIn() 1658 */ 1659 // This method uses the term 'zoom' for legacy reasons, but relates 1660 // to what chrome calls the 'page scale factor'. canZoomIn()1661 public boolean canZoomIn() { 1662 final float zoomInExtent = mMaxPageScaleFactor - mPageScaleFactor; 1663 return zoomInExtent > ZOOM_CONTROLS_EPSILON; 1664 } 1665 1666 /** 1667 * @see android.webkit.WebView#canZoomOut() 1668 */ 1669 // This method uses the term 'zoom' for legacy reasons, but relates 1670 // to what chrome calls the 'page scale factor'. canZoomOut()1671 public boolean canZoomOut() { 1672 final float zoomOutExtent = mPageScaleFactor - mMinPageScaleFactor; 1673 return zoomOutExtent > ZOOM_CONTROLS_EPSILON; 1674 } 1675 1676 /** 1677 * @see android.webkit.WebView#zoomIn() 1678 */ 1679 // This method uses the term 'zoom' for legacy reasons, but relates 1680 // to what chrome calls the 'page scale factor'. zoomIn()1681 public boolean zoomIn() { 1682 if (!canZoomIn()) { 1683 return false; 1684 } 1685 return zoomBy(1.25f); 1686 } 1687 1688 /** 1689 * @see android.webkit.WebView#zoomOut() 1690 */ 1691 // This method uses the term 'zoom' for legacy reasons, but relates 1692 // to what chrome calls the 'page scale factor'. zoomOut()1693 public boolean zoomOut() { 1694 if (!canZoomOut()) { 1695 return false; 1696 } 1697 return zoomBy(0.8f); 1698 } 1699 1700 /** 1701 * @see android.webkit.WebView#zoomBy() 1702 */ 1703 // This method uses the term 'zoom' for legacy reasons, but relates 1704 // to what chrome calls the 'page scale factor'. zoomBy(float delta)1705 public boolean zoomBy(float delta) { 1706 if (isDestroyed()) return false; 1707 if (delta < 0.01f || delta > 100.0f) { 1708 throw new IllegalStateException("zoom delta value outside [0.01, 100] range."); 1709 } 1710 return mContentViewCore.pinchByDelta(delta); 1711 } 1712 1713 /** 1714 * @see android.webkit.WebView#invokeZoomPicker() 1715 */ invokeZoomPicker()1716 public void invokeZoomPicker() { 1717 if (!isDestroyed()) mContentViewCore.invokeZoomPicker(); 1718 } 1719 1720 /** 1721 * @see android.webkit.WebView#preauthorizePermission(Uri, long) 1722 */ preauthorizePermission(Uri origin, long resources)1723 public void preauthorizePermission(Uri origin, long resources) { 1724 if (isDestroyed()) return; 1725 nativePreauthorizePermission(mNativeAwContents, origin.toString(), resources); 1726 } 1727 1728 /** 1729 * @see ContentViewCore.evaluateJavaScript(String, JavaScriptCallback) 1730 */ evaluateJavaScript(String script, final ValueCallback<String> callback)1731 public void evaluateJavaScript(String script, final ValueCallback<String> callback) { 1732 if (isDestroyed()) return; 1733 JavaScriptCallback jsCallback = null; 1734 if (callback != null) { 1735 jsCallback = new JavaScriptCallback() { 1736 @Override 1737 public void handleJavaScriptResult(String jsonResult) { 1738 callback.onReceiveValue(jsonResult); 1739 } 1740 }; 1741 } 1742 1743 mWebContents.evaluateJavaScript(script, jsCallback); 1744 } 1745 1746 // TODO(boliu): Remove this once Android side no longer calls this. evaluateJavaScriptEvenIfNotYetNavigated(String script)1747 public void evaluateJavaScriptEvenIfNotYetNavigated(String script) { 1748 if (!isDestroyed()) mWebContents.evaluateJavaScript(script, null); 1749 } 1750 1751 //-------------------------------------------------------------------------------------------- 1752 // View and ViewGroup method implementations 1753 //-------------------------------------------------------------------------------------------- 1754 1755 /** 1756 * @see android.webkit.View#onTouchEvent() 1757 */ onTouchEvent(MotionEvent event)1758 public boolean onTouchEvent(MotionEvent event) { 1759 return mAwViewMethods.onTouchEvent(event); 1760 } 1761 1762 /** 1763 * @see android.view.View#onHoverEvent() 1764 */ onHoverEvent(MotionEvent event)1765 public boolean onHoverEvent(MotionEvent event) { 1766 return mAwViewMethods.onHoverEvent(event); 1767 } 1768 1769 /** 1770 * @see android.view.View#onGenericMotionEvent() 1771 */ onGenericMotionEvent(MotionEvent event)1772 public boolean onGenericMotionEvent(MotionEvent event) { 1773 return isDestroyed() ? false : mContentViewCore.onGenericMotionEvent(event); 1774 } 1775 1776 /** 1777 * @see android.view.View#onConfigurationChanged() 1778 */ onConfigurationChanged(Configuration newConfig)1779 public void onConfigurationChanged(Configuration newConfig) { 1780 mAwViewMethods.onConfigurationChanged(newConfig); 1781 } 1782 1783 /** 1784 * @see android.view.View#onAttachedToWindow() 1785 */ onAttachedToWindow()1786 public void onAttachedToWindow() { 1787 mTemporarilyDetached = false; 1788 mAwViewMethods.onAttachedToWindow(); 1789 } 1790 1791 /** 1792 * @see android.view.View#onDetachedFromWindow() 1793 */ 1794 @SuppressLint("MissingSuperCall") onDetachedFromWindow()1795 public void onDetachedFromWindow() { 1796 mAwViewMethods.onDetachedFromWindow(); 1797 } 1798 1799 /** 1800 * @see android.view.View#onWindowFocusChanged() 1801 */ onWindowFocusChanged(boolean hasWindowFocus)1802 public void onWindowFocusChanged(boolean hasWindowFocus) { 1803 mAwViewMethods.onWindowFocusChanged(hasWindowFocus); 1804 } 1805 1806 /** 1807 * @see android.view.View#onFocusChanged() 1808 */ onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)1809 public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 1810 if (!mTemporarilyDetached) { 1811 mAwViewMethods.onFocusChanged(focused, direction, previouslyFocusedRect); 1812 } 1813 } 1814 1815 /** 1816 * @see android.view.View#onStartTemporaryDetach() 1817 */ onStartTemporaryDetach()1818 public void onStartTemporaryDetach() { 1819 mTemporarilyDetached = true; 1820 } 1821 1822 /** 1823 * @see android.view.View#onFinishTemporaryDetach() 1824 */ onFinishTemporaryDetach()1825 public void onFinishTemporaryDetach() { 1826 mTemporarilyDetached = false; 1827 } 1828 1829 /** 1830 * @see android.view.View#onSizeChanged() 1831 */ onSizeChanged(int w, int h, int ow, int oh)1832 public void onSizeChanged(int w, int h, int ow, int oh) { 1833 mAwViewMethods.onSizeChanged(w, h, ow, oh); 1834 } 1835 1836 /** 1837 * @see android.view.View#onVisibilityChanged() 1838 */ onVisibilityChanged(View changedView, int visibility)1839 public void onVisibilityChanged(View changedView, int visibility) { 1840 mAwViewMethods.onVisibilityChanged(changedView, visibility); 1841 } 1842 1843 /** 1844 * @see android.view.View#onWindowVisibilityChanged() 1845 */ onWindowVisibilityChanged(int visibility)1846 public void onWindowVisibilityChanged(int visibility) { 1847 mAwViewMethods.onWindowVisibilityChanged(visibility); 1848 } 1849 setViewVisibilityInternal(boolean visible)1850 private void setViewVisibilityInternal(boolean visible) { 1851 mIsViewVisible = visible; 1852 if (!isDestroyed()) nativeSetViewVisibility(mNativeAwContents, mIsViewVisible); 1853 } 1854 setWindowVisibilityInternal(boolean visible)1855 private void setWindowVisibilityInternal(boolean visible) { 1856 mIsWindowVisible = visible; 1857 if (!isDestroyed()) nativeSetWindowVisibility(mNativeAwContents, mIsWindowVisible); 1858 } 1859 1860 /** 1861 * Key for opaque state in bundle. Note this is only public for tests. 1862 */ 1863 public static final String SAVE_RESTORE_STATE_KEY = "WEBVIEW_CHROMIUM_STATE"; 1864 1865 /** 1866 * Save the state of this AwContents into provided Bundle. 1867 * @return False if saving state failed. 1868 */ saveState(Bundle outState)1869 public boolean saveState(Bundle outState) { 1870 if (isDestroyed() || outState == null) return false; 1871 1872 byte[] state = nativeGetOpaqueState(mNativeAwContents); 1873 if (state == null) return false; 1874 1875 outState.putByteArray(SAVE_RESTORE_STATE_KEY, state); 1876 return true; 1877 } 1878 1879 /** 1880 * Restore the state of this AwContents into provided Bundle. 1881 * @param inState Must be a bundle returned by saveState. 1882 * @return False if restoring state failed. 1883 */ restoreState(Bundle inState)1884 public boolean restoreState(Bundle inState) { 1885 if (isDestroyed() || inState == null) return false; 1886 1887 byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY); 1888 if (state == null) return false; 1889 1890 boolean result = nativeRestoreFromOpaqueState(mNativeAwContents, state); 1891 1892 // The onUpdateTitle callback normally happens when a page is loaded, 1893 // but is optimized out in the restoreState case because the title is 1894 // already restored. See WebContentsImpl::UpdateTitleForEntry. So we 1895 // call the callback explicitly here. 1896 if (result) mContentsClient.onReceivedTitle(mWebContents.getTitle()); 1897 1898 return result; 1899 } 1900 1901 /** 1902 * @see ContentViewCore#addPossiblyUnsafeJavascriptInterface(Object, String, Class) 1903 */ addPossiblyUnsafeJavascriptInterface(Object object, String name, Class<? extends Annotation> requiredAnnotation)1904 public void addPossiblyUnsafeJavascriptInterface(Object object, String name, 1905 Class<? extends Annotation> requiredAnnotation) { 1906 if (isDestroyed()) return; 1907 mContentViewCore.addPossiblyUnsafeJavascriptInterface(object, name, requiredAnnotation); 1908 } 1909 1910 /** 1911 * @see android.webkit.WebView#removeJavascriptInterface(String) 1912 */ removeJavascriptInterface(String interfaceName)1913 public void removeJavascriptInterface(String interfaceName) { 1914 if (!isDestroyed()) mContentViewCore.removeJavascriptInterface(interfaceName); 1915 } 1916 1917 /** 1918 * If native accessibility (not script injection) is enabled, and if this is 1919 * running on JellyBean or later, returns an AccessibilityNodeProvider that 1920 * implements native accessibility for this view. Returns null otherwise. 1921 * @return The AccessibilityNodeProvider, if available, or null otherwise. 1922 */ getAccessibilityNodeProvider()1923 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 1924 return isDestroyed() ? null : mContentViewCore.getAccessibilityNodeProvider(); 1925 } 1926 1927 /** 1928 * @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo) 1929 */ onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)1930 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 1931 if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityNodeInfo(info); 1932 } 1933 1934 /** 1935 * @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent) 1936 */ onInitializeAccessibilityEvent(AccessibilityEvent event)1937 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 1938 if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityEvent(event); 1939 } 1940 supportsAccessibilityAction(int action)1941 public boolean supportsAccessibilityAction(int action) { 1942 return isDestroyed() ? false : mContentViewCore.supportsAccessibilityAction(action); 1943 } 1944 1945 /** 1946 * @see android.webkit.WebView#performAccessibilityAction(int, Bundle) 1947 */ performAccessibilityAction(int action, Bundle arguments)1948 public boolean performAccessibilityAction(int action, Bundle arguments) { 1949 return isDestroyed() ? false 1950 : mContentViewCore.performAccessibilityAction(action, arguments); 1951 } 1952 1953 /** 1954 * @see android.webkit.WebView#clearFormData() 1955 */ hideAutofillPopup()1956 public void hideAutofillPopup() { 1957 if (mAwAutofillClient != null) { 1958 mAwAutofillClient.hideAutofillPopup(); 1959 } 1960 } 1961 setNetworkAvailable(boolean networkUp)1962 public void setNetworkAvailable(boolean networkUp) { 1963 if (!isDestroyed()) nativeSetJsOnlineProperty(mNativeAwContents, networkUp); 1964 } 1965 1966 //-------------------------------------------------------------------------------------------- 1967 // Methods called from native via JNI 1968 //-------------------------------------------------------------------------------------------- 1969 1970 @CalledByNative onDocumentHasImagesResponse(boolean result, Message message)1971 private static void onDocumentHasImagesResponse(boolean result, Message message) { 1972 message.arg1 = result ? 1 : 0; 1973 message.sendToTarget(); 1974 } 1975 1976 @CalledByNative onReceivedTouchIconUrl(String url, boolean precomposed)1977 private void onReceivedTouchIconUrl(String url, boolean precomposed) { 1978 mContentsClient.onReceivedTouchIconUrl(url, precomposed); 1979 } 1980 1981 @CalledByNative onReceivedIcon(Bitmap bitmap)1982 private void onReceivedIcon(Bitmap bitmap) { 1983 mContentsClient.onReceivedIcon(bitmap); 1984 mFavicon = bitmap; 1985 } 1986 1987 /** Callback for generateMHTML. */ 1988 @CalledByNative generateMHTMLCallback( String path, long size, ValueCallback<String> callback)1989 private static void generateMHTMLCallback( 1990 String path, long size, ValueCallback<String> callback) { 1991 if (callback == null) return; 1992 callback.onReceiveValue(size < 0 ? null : path); 1993 } 1994 1995 @CalledByNative 1996 private void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) { 1997 mContentsClient.onReceivedHttpAuthRequest(handler, host, realm); 1998 } 1999 2000 private class AwGeolocationCallback implements GeolocationPermissions.Callback { 2001 2002 @Override 2003 public void invoke(final String origin, final boolean allow, final boolean retain) { 2004 ThreadUtils.runOnUiThread(new Runnable() { 2005 @Override 2006 public void run() { 2007 if (retain) { 2008 if (allow) { 2009 mBrowserContext.getGeolocationPermissions().allow(origin); 2010 } else { 2011 mBrowserContext.getGeolocationPermissions().deny(origin); 2012 } 2013 } 2014 if (isDestroyed()) return; 2015 nativeInvokeGeolocationCallback(mNativeAwContents, allow, origin); 2016 } 2017 }); 2018 } 2019 } 2020 2021 @CalledByNative 2022 private void onGeolocationPermissionsShowPrompt(String origin) { 2023 if (isDestroyed()) return; 2024 AwGeolocationPermissions permissions = mBrowserContext.getGeolocationPermissions(); 2025 // Reject if geoloaction is disabled, or the origin has a retained deny 2026 if (!mSettings.getGeolocationEnabled()) { 2027 nativeInvokeGeolocationCallback(mNativeAwContents, false, origin); 2028 return; 2029 } 2030 // Allow if the origin has a retained allow 2031 if (permissions.hasOrigin(origin)) { 2032 nativeInvokeGeolocationCallback(mNativeAwContents, permissions.isOriginAllowed(origin), 2033 origin); 2034 return; 2035 } 2036 mContentsClient.onGeolocationPermissionsShowPrompt( 2037 origin, new AwGeolocationCallback()); 2038 } 2039 2040 @CalledByNative 2041 private void onGeolocationPermissionsHidePrompt() { 2042 mContentsClient.onGeolocationPermissionsHidePrompt(); 2043 } 2044 2045 @CalledByNative 2046 private void onPermissionRequest(AwPermissionRequest awPermissionRequest) { 2047 mContentsClient.onPermissionRequest(awPermissionRequest); 2048 } 2049 2050 @CalledByNative 2051 private void onPermissionRequestCanceled(AwPermissionRequest awPermissionRequest) { 2052 mContentsClient.onPermissionRequestCanceled(awPermissionRequest); 2053 } 2054 2055 @CalledByNative 2056 public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, 2057 boolean isDoneCounting) { 2058 mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting); 2059 } 2060 2061 @CalledByNative 2062 public void onNewPicture() { 2063 // Don't call capturePicture() here but instead defer it until the posted task runs within 2064 // the callback helper, to avoid doubling back into the renderer compositor in the middle 2065 // of the notification it is sending up to here. 2066 mContentsClient.getCallbackHelper().postOnNewPicture(mPictureListenerContentProvider); 2067 } 2068 2069 // Called as a result of nativeUpdateLastHitTestData. 2070 @CalledByNative 2071 private void updateHitTestData( 2072 int type, String extra, String href, String anchorText, String imgSrc) { 2073 mPossiblyStaleHitTestData.hitTestResultType = type; 2074 mPossiblyStaleHitTestData.hitTestResultExtraData = extra; 2075 mPossiblyStaleHitTestData.href = href; 2076 mPossiblyStaleHitTestData.anchorText = anchorText; 2077 mPossiblyStaleHitTestData.imgSrc = imgSrc; 2078 } 2079 2080 @CalledByNative 2081 private boolean requestDrawGL(Canvas canvas, boolean waitForCompletion) { 2082 return mNativeGLDelegate.requestDrawGL(canvas, waitForCompletion, mContainerView); 2083 } 2084 2085 private static final boolean SUPPORTS_ON_ANIMATION = 2086 Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; 2087 2088 @CalledByNative 2089 private void postInvalidateOnAnimation() { 2090 if (SUPPORTS_ON_ANIMATION && !mWindowAndroid.isInsideVSync()) { 2091 mContainerView.postInvalidateOnAnimation(); 2092 } else { 2093 mContainerView.invalidate(); 2094 } 2095 } 2096 2097 // Call postInvalidateOnAnimation for invalidations. This is only used to synchronize 2098 // draw functor destruction. 2099 @CalledByNative 2100 private void invalidateOnFunctorDestroy() { 2101 mContainerView.invalidate(); 2102 } 2103 2104 @CalledByNative 2105 private int[] getLocationOnScreen() { 2106 int[] result = new int[2]; 2107 mContainerView.getLocationOnScreen(result); 2108 return result; 2109 } 2110 2111 @CalledByNative 2112 private void onWebLayoutPageScaleFactorChanged(float webLayoutPageScaleFactor) { 2113 // This change notification comes from the renderer thread, not from the cc/ impl thread. 2114 mLayoutSizer.onPageScaleChanged(webLayoutPageScaleFactor); 2115 } 2116 2117 @CalledByNative 2118 private void onWebLayoutContentsSizeChanged(int widthCss, int heightCss) { 2119 // This change notification comes from the renderer thread, not from the cc/ impl thread. 2120 mLayoutSizer.onContentSizeChanged(widthCss, heightCss); 2121 } 2122 2123 @CalledByNative 2124 private void scrollContainerViewTo(int x, int y) { 2125 mScrollOffsetManager.scrollContainerViewTo(x, y); 2126 } 2127 2128 @CalledByNative 2129 private boolean isFlingActive() { 2130 return mScrollOffsetManager.isFlingActive(); 2131 } 2132 2133 @CalledByNative 2134 private void updateScrollState(int maxContainerViewScrollOffsetX, 2135 int maxContainerViewScrollOffsetY, int contentWidthDip, int contentHeightDip, 2136 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) { 2137 mContentWidthDip = contentWidthDip; 2138 mContentHeightDip = contentHeightDip; 2139 mScrollOffsetManager.setMaxScrollOffset(maxContainerViewScrollOffsetX, 2140 maxContainerViewScrollOffsetY); 2141 setPageScaleFactorAndLimits(pageScaleFactor, minPageScaleFactor, maxPageScaleFactor); 2142 } 2143 2144 @CalledByNative 2145 private void setAwAutofillClient(AwAutofillClient client) { 2146 mAwAutofillClient = client; 2147 client.init(mContentViewCore); 2148 } 2149 2150 @CalledByNative 2151 private void didOverscroll(int deltaX, int deltaY) { 2152 if (mOverScrollGlow != null) { 2153 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY); 2154 } 2155 2156 mScrollOffsetManager.overScrollBy(deltaX, deltaY); 2157 2158 if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) { 2159 mContainerView.invalidate(); 2160 } 2161 } 2162 2163 // ------------------------------------------------------------------------------------------- 2164 // Helper methods 2165 // ------------------------------------------------------------------------------------------- 2166 2167 private void setPageScaleFactorAndLimits( 2168 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) { 2169 if (mPageScaleFactor == pageScaleFactor && 2170 mMinPageScaleFactor == minPageScaleFactor && 2171 mMaxPageScaleFactor == maxPageScaleFactor) { 2172 return; 2173 } 2174 mMinPageScaleFactor = minPageScaleFactor; 2175 mMaxPageScaleFactor = maxPageScaleFactor; 2176 if (mPageScaleFactor != pageScaleFactor) { 2177 float oldPageScaleFactor = mPageScaleFactor; 2178 mPageScaleFactor = pageScaleFactor; 2179 mContentsClient.getCallbackHelper().postOnScaleChangedScaled( 2180 (float) (oldPageScaleFactor * mDIPScale), 2181 (float) (mPageScaleFactor * mDIPScale)); 2182 } 2183 } 2184 2185 private void saveWebArchiveInternal(String path, final ValueCallback<String> callback) { 2186 if (path == null || isDestroyed()) { 2187 ThreadUtils.runOnUiThread(new Runnable() { 2188 @Override 2189 public void run() { 2190 callback.onReceiveValue(null); 2191 } 2192 }); 2193 } else { 2194 nativeGenerateMHTML(mNativeAwContents, path, callback); 2195 } 2196 } 2197 2198 /** 2199 * Try to generate a pathname for saving an MHTML archive. This roughly follows WebView's 2200 * autoname logic. 2201 */ 2202 private static String generateArchiveAutoNamePath(String originalUrl, String baseName) { 2203 String name = null; 2204 if (originalUrl != null && !originalUrl.isEmpty()) { 2205 try { 2206 String path = new URL(originalUrl).getPath(); 2207 int lastSlash = path.lastIndexOf('/'); 2208 if (lastSlash > 0) { 2209 name = path.substring(lastSlash + 1); 2210 } else { 2211 name = path; 2212 } 2213 } catch (MalformedURLException e) { 2214 // If it fails parsing the URL, we'll just rely on the default name below. 2215 } 2216 } 2217 2218 if (TextUtils.isEmpty(name)) name = "index"; 2219 2220 String testName = baseName + name + WEB_ARCHIVE_EXTENSION; 2221 if (!new File(testName).exists()) return testName; 2222 2223 for (int i = 1; i < 100; i++) { 2224 testName = baseName + name + "-" + i + WEB_ARCHIVE_EXTENSION; 2225 if (!new File(testName).exists()) return testName; 2226 } 2227 2228 Log.e(TAG, "Unable to auto generate archive name for path: " + baseName); 2229 return null; 2230 } 2231 2232 @Override 2233 public void extractSmartClipData(int x, int y, int width, int height) { 2234 if (!isDestroyed()) mContentViewCore.extractSmartClipData(x, y, width, height); 2235 } 2236 2237 @Override 2238 public void setSmartClipResultHandler(final Handler resultHandler) { 2239 if (resultHandler == null) { 2240 mContentViewCore.setSmartClipDataListener(null); 2241 return; 2242 } 2243 mContentViewCore.setSmartClipDataListener(new ContentViewCore.SmartClipDataListener() { 2244 public void onSmartClipDataExtracted(String text, String html, Rect clipRect) { 2245 Bundle bundle = new Bundle(); 2246 bundle.putString("url", mContentViewCore.getWebContents().getVisibleUrl()); 2247 bundle.putString("title", mContentViewCore.getWebContents().getTitle()); 2248 bundle.putParcelable("rect", clipRect); 2249 bundle.putString("text", text); 2250 bundle.putString("html", html); 2251 try { 2252 Message msg = Message.obtain(resultHandler, 0); 2253 msg.setData(bundle); 2254 msg.sendToTarget(); 2255 } catch (Exception e) { 2256 Log.e(TAG, "Error calling handler for smart clip data: ", e); 2257 } 2258 } 2259 }); 2260 } 2261 2262 // -------------------------------------------------------------------------------------------- 2263 // This is the AwViewMethods implementation that does real work. The AwViewMethodsImpl is 2264 // hooked up to the WebView in embedded mode and to the FullScreenView in fullscreen mode, 2265 // but not to both at the same time. 2266 private class AwViewMethodsImpl implements AwViewMethods { 2267 private int mLayerType = View.LAYER_TYPE_NONE; 2268 private ComponentCallbacks2 mComponentCallbacks; 2269 2270 // Only valid within software onDraw(). 2271 private final Rect mClipBoundsTemporary = new Rect(); 2272 2273 @Override 2274 public void onDraw(Canvas canvas) { 2275 if (isDestroyed()) { 2276 canvas.drawColor(getEffectiveBackgroundColor()); 2277 return; 2278 } 2279 2280 // For hardware draws, the clip at onDraw time could be different 2281 // from the clip during DrawGL. 2282 if (!canvas.isHardwareAccelerated() && !canvas.getClipBounds(mClipBoundsTemporary)) { 2283 return; 2284 } 2285 2286 mScrollOffsetManager.syncScrollOffsetFromOnDraw(); 2287 Rect globalVisibleRect = getGlobalVisibleRect(); 2288 if (!nativeOnDraw(mNativeAwContents, canvas, canvas.isHardwareAccelerated(), 2289 mContainerView.getScrollX(), mContainerView.getScrollY(), 2290 globalVisibleRect.left, globalVisibleRect.top, 2291 globalVisibleRect.right, globalVisibleRect.bottom)) { 2292 // Can happen during initialization when compositor is not set 2293 // up. Or when clearView 2294 // is in effect. Just draw background color instead. 2295 canvas.drawColor(getEffectiveBackgroundColor()); 2296 } 2297 2298 if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas, 2299 mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), 2300 mScrollOffsetManager.computeMaximumVerticalScrollOffset())) { 2301 mContainerView.invalidate(); 2302 } 2303 } 2304 2305 @Override 2306 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 2307 mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec); 2308 } 2309 2310 @Override 2311 public void requestFocus() { 2312 if (isDestroyed()) return; 2313 if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) { 2314 nativeFocusFirstNode(mNativeAwContents); 2315 } 2316 } 2317 2318 @Override 2319 public void setLayerType(int layerType, Paint paint) { 2320 mLayerType = layerType; 2321 updateHardwareAcceleratedFeaturesToggle(); 2322 } 2323 2324 private void updateHardwareAcceleratedFeaturesToggle() { 2325 mSettings.setEnableSupportedHardwareAcceleratedFeatures( 2326 mIsAttachedToWindow && mContainerView.isHardwareAccelerated() && 2327 (mLayerType == View.LAYER_TYPE_NONE 2328 || mLayerType == View.LAYER_TYPE_HARDWARE)); 2329 } 2330 2331 @Override 2332 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 2333 return isDestroyed() ? null : mContentViewCore.onCreateInputConnection(outAttrs); 2334 } 2335 2336 @Override 2337 public boolean onKeyUp(int keyCode, KeyEvent event) { 2338 return isDestroyed() ? false : mContentViewCore.onKeyUp(keyCode, event); 2339 } 2340 2341 @Override 2342 public boolean dispatchKeyEvent(KeyEvent event) { 2343 if (isDestroyed()) return false; 2344 if (isDpadEvent(event)) { 2345 mSettings.setSpatialNavigationEnabled(true); 2346 } 2347 return mContentViewCore.dispatchKeyEvent(event); 2348 } 2349 2350 private boolean isDpadEvent(KeyEvent event) { 2351 if (event.getAction() == KeyEvent.ACTION_DOWN) { 2352 switch (event.getKeyCode()) { 2353 case KeyEvent.KEYCODE_DPAD_CENTER: 2354 case KeyEvent.KEYCODE_DPAD_DOWN: 2355 case KeyEvent.KEYCODE_DPAD_UP: 2356 case KeyEvent.KEYCODE_DPAD_LEFT: 2357 case KeyEvent.KEYCODE_DPAD_RIGHT: 2358 return true; 2359 } 2360 } 2361 return false; 2362 } 2363 2364 @Override 2365 public boolean onTouchEvent(MotionEvent event) { 2366 if (isDestroyed()) return false; 2367 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 2368 mSettings.setSpatialNavigationEnabled(false); 2369 } 2370 2371 mScrollOffsetManager.setProcessingTouchEvent(true); 2372 boolean rv = mContentViewCore.onTouchEvent(event); 2373 mScrollOffsetManager.setProcessingTouchEvent(false); 2374 2375 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 2376 int actionIndex = event.getActionIndex(); 2377 2378 // Note this will trigger IPC back to browser even if nothing is 2379 // hit. 2380 nativeRequestNewHitTestDataAt(mNativeAwContents, 2381 (int) Math.round(event.getX(actionIndex) / mDIPScale), 2382 (int) Math.round(event.getY(actionIndex) / mDIPScale)); 2383 } 2384 2385 if (mOverScrollGlow != null) { 2386 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 2387 mOverScrollGlow.setShouldPull(true); 2388 } else if (event.getActionMasked() == MotionEvent.ACTION_UP || 2389 event.getActionMasked() == MotionEvent.ACTION_CANCEL) { 2390 mOverScrollGlow.setShouldPull(false); 2391 mOverScrollGlow.releaseAll(); 2392 } 2393 } 2394 2395 return rv; 2396 } 2397 2398 @Override 2399 public boolean onHoverEvent(MotionEvent event) { 2400 return isDestroyed() ? false : mContentViewCore.onHoverEvent(event); 2401 } 2402 2403 @Override 2404 public boolean onGenericMotionEvent(MotionEvent event) { 2405 return isDestroyed() ? false : mContentViewCore.onGenericMotionEvent(event); 2406 } 2407 2408 @Override 2409 public void onConfigurationChanged(Configuration newConfig) { 2410 if (!isDestroyed()) mContentViewCore.onConfigurationChanged(newConfig); 2411 } 2412 2413 @Override 2414 public void onAttachedToWindow() { 2415 if (isDestroyed()) return; 2416 if (mIsAttachedToWindow) { 2417 Log.w(TAG, "onAttachedToWindow called when already attached. Ignoring"); 2418 return; 2419 } 2420 mIsAttachedToWindow = true; 2421 2422 mContentViewCore.onAttachedToWindow(); 2423 nativeOnAttachedToWindow(mNativeAwContents, mContainerView.getWidth(), 2424 mContainerView.getHeight()); 2425 updateHardwareAcceleratedFeaturesToggle(); 2426 2427 if (mComponentCallbacks != null) return; 2428 mComponentCallbacks = new AwComponentCallbacks(); 2429 mContext.registerComponentCallbacks(mComponentCallbacks); 2430 } 2431 2432 @Override 2433 public void onDetachedFromWindow() { 2434 if (isDestroyed()) return; 2435 if (!mIsAttachedToWindow) { 2436 Log.w(TAG, "onDetachedFromWindow called when already detached. Ignoring"); 2437 return; 2438 } 2439 mIsAttachedToWindow = false; 2440 hideAutofillPopup(); 2441 nativeOnDetachedFromWindow(mNativeAwContents); 2442 2443 mContentViewCore.onDetachedFromWindow(); 2444 updateHardwareAcceleratedFeaturesToggle(); 2445 2446 if (mComponentCallbacks != null) { 2447 mContext.unregisterComponentCallbacks(mComponentCallbacks); 2448 mComponentCallbacks = null; 2449 } 2450 2451 mScrollAccessibilityHelper.removePostedCallbacks(); 2452 mNativeGLDelegate.detachGLFunctor(); 2453 } 2454 2455 @Override 2456 public void onWindowFocusChanged(boolean hasWindowFocus) { 2457 if (isDestroyed()) return; 2458 mWindowFocused = hasWindowFocus; 2459 mContentViewCore.onWindowFocusChanged(hasWindowFocus); 2460 } 2461 2462 @Override 2463 public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 2464 if (isDestroyed()) return; 2465 mContainerViewFocused = focused; 2466 mContentViewCore.onFocusChanged(focused); 2467 } 2468 2469 @Override 2470 public void onSizeChanged(int w, int h, int ow, int oh) { 2471 if (isDestroyed()) return; 2472 mScrollOffsetManager.setContainerViewSize(w, h); 2473 // The AwLayoutSizer needs to go first so that if we're in 2474 // fixedLayoutSize mode the update 2475 // to enter fixedLayoutSize mode is sent before the first resize 2476 // update. 2477 mLayoutSizer.onSizeChanged(w, h, ow, oh); 2478 mContentViewCore.onPhysicalBackingSizeChanged(w, h); 2479 mContentViewCore.onSizeChanged(w, h, ow, oh); 2480 nativeOnSizeChanged(mNativeAwContents, w, h, ow, oh); 2481 } 2482 2483 @Override 2484 public void onVisibilityChanged(View changedView, int visibility) { 2485 boolean viewVisible = mContainerView.getVisibility() == View.VISIBLE; 2486 if (mIsViewVisible == viewVisible) return; 2487 setViewVisibilityInternal(viewVisible); 2488 } 2489 2490 @Override 2491 public void onWindowVisibilityChanged(int visibility) { 2492 boolean windowVisible = visibility == View.VISIBLE; 2493 if (mIsWindowVisible == windowVisible) return; 2494 setWindowVisibilityInternal(windowVisible); 2495 } 2496 } 2497 2498 // Return true if the GeolocationPermissionAPI should be used. 2499 @CalledByNative 2500 private boolean useLegacyGeolocationPermissionAPI() { 2501 // Always return true since we are not ready to swap the geolocation yet. 2502 // TODO: If we decide not to migrate the geolocation, there are some unreachable 2503 // code need to remove. http://crbug.com/396184. 2504 return true; 2505 } 2506 2507 //-------------------------------------------------------------------------------------------- 2508 // Native methods 2509 //-------------------------------------------------------------------------------------------- 2510 2511 private static native long nativeInit(AwBrowserContext browserContext); 2512 private static native void nativeDestroy(long nativeAwContents); 2513 private static native void nativeSetAwDrawSWFunctionTable(long functionTablePointer); 2514 private static native void nativeSetAwDrawGLFunctionTable(long functionTablePointer); 2515 private static native long nativeGetAwDrawGLFunction(); 2516 private static native int nativeGetNativeInstanceCount(); 2517 private static native void nativeSetShouldDownloadFavicons(); 2518 2519 private native void nativeSetJavaPeers(long nativeAwContents, AwContents awContents, 2520 AwWebContentsDelegate webViewWebContentsDelegate, 2521 AwContentsClientBridge contentsClientBridge, 2522 AwContentsIoThreadClient ioThreadClient, 2523 InterceptNavigationDelegate navigationInterceptionDelegate); 2524 private native long nativeGetWebContents(long nativeAwContents); 2525 2526 private native void nativeDocumentHasImages(long nativeAwContents, Message message); 2527 private native void nativeGenerateMHTML( 2528 long nativeAwContents, String path, ValueCallback<String> callback); 2529 2530 private native void nativeAddVisitedLinks(long nativeAwContents, String[] visitedLinks); 2531 private native boolean nativeOnDraw(long nativeAwContents, Canvas canvas, 2532 boolean isHardwareAccelerated, int scrollX, int scrollY, 2533 int visibleLeft, int visibleTop, int visibleRight, int visibleBottom); 2534 private native void nativeFindAllAsync(long nativeAwContents, String searchString); 2535 private native void nativeFindNext(long nativeAwContents, boolean forward); 2536 private native void nativeClearMatches(long nativeAwContents); 2537 private native void nativeClearCache(long nativeAwContents, boolean includeDiskFiles); 2538 private native byte[] nativeGetCertificate(long nativeAwContents); 2539 2540 // Coordinates in desity independent pixels. 2541 private native void nativeRequestNewHitTestDataAt(long nativeAwContents, int x, int y); 2542 private native void nativeUpdateLastHitTestData(long nativeAwContents); 2543 2544 private native void nativeOnSizeChanged(long nativeAwContents, int w, int h, int ow, int oh); 2545 private native void nativeScrollTo(long nativeAwContents, int x, int y); 2546 private native void nativeSetViewVisibility(long nativeAwContents, boolean visible); 2547 private native void nativeSetWindowVisibility(long nativeAwContents, boolean visible); 2548 private native void nativeSetIsPaused(long nativeAwContents, boolean paused); 2549 private native void nativeOnAttachedToWindow(long nativeAwContents, int w, int h); 2550 private static native void nativeOnDetachedFromWindow(long nativeAwContents); 2551 private native void nativeSetDipScale(long nativeAwContents, float dipScale); 2552 2553 // Returns null if save state fails. 2554 private native byte[] nativeGetOpaqueState(long nativeAwContents); 2555 2556 // Returns false if restore state fails. 2557 private native boolean nativeRestoreFromOpaqueState(long nativeAwContents, byte[] state); 2558 2559 private native long nativeReleasePopupAwContents(long nativeAwContents); 2560 private native void nativeFocusFirstNode(long nativeAwContents); 2561 private native void nativeSetBackgroundColor(long nativeAwContents, int color); 2562 2563 private native long nativeGetAwDrawGLViewContext(long nativeAwContents); 2564 private native long nativeCapturePicture(long nativeAwContents, int width, int height); 2565 private native void nativeEnableOnNewPicture(long nativeAwContents, boolean enabled); 2566 private native void nativeClearView(long nativeAwContents); 2567 private native void nativeSetExtraHeadersForUrl(long nativeAwContents, 2568 String url, String extraHeaders); 2569 2570 private native void nativeInvokeGeolocationCallback( 2571 long nativeAwContents, boolean value, String requestingFrame); 2572 2573 private native void nativeSetJsOnlineProperty(long nativeAwContents, boolean networkUp); 2574 2575 private native void nativeTrimMemory(long nativeAwContents, int level, boolean visible); 2576 2577 private native void nativeCreatePdfExporter(long nativeAwContents, AwPdfExporter awPdfExporter); 2578 2579 private native void nativePreauthorizePermission(long nativeAwContents, String origin, 2580 long resources); 2581 } 2582