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.annotation.TargetApi; 9 import android.content.ComponentCallbacks2; 10 import android.content.Context; 11 import android.content.Intent; 12 import android.content.res.Configuration; 13 import android.graphics.Bitmap; 14 import android.graphics.Canvas; 15 import android.graphics.Color; 16 import android.graphics.ColorMatrix; 17 import android.graphics.ColorMatrixColorFilter; 18 import android.graphics.Paint; 19 import android.graphics.Picture; 20 import android.graphics.Rect; 21 import android.net.Uri; 22 import android.net.http.SslCertificate; 23 import android.os.Build; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.os.SystemClock; 28 import android.text.TextUtils; 29 import android.util.Base64; 30 import android.util.Pair; 31 import android.util.SparseArray; 32 import android.view.DragEvent; 33 import android.view.KeyEvent; 34 import android.view.MotionEvent; 35 import android.view.View; 36 import android.view.ViewGroup; 37 import android.view.ViewStructure; 38 import android.view.ViewTreeObserver; 39 import android.view.accessibility.AccessibilityNodeProvider; 40 import android.view.animation.AnimationUtils; 41 import android.view.autofill.AutofillValue; 42 import android.view.inputmethod.EditorInfo; 43 import android.view.inputmethod.InputConnection; 44 import android.view.textclassifier.TextClassifier; 45 import android.webkit.JavascriptInterface; 46 47 import androidx.annotation.IntDef; 48 import androidx.annotation.NonNull; 49 import androidx.annotation.VisibleForTesting; 50 51 import org.chromium.android_webview.common.AwFeatures; 52 import org.chromium.android_webview.common.AwSwitches; 53 import org.chromium.android_webview.gfx.AwDrawFnImpl; 54 import org.chromium.android_webview.gfx.AwFunctor; 55 import org.chromium.android_webview.gfx.AwGLFunctor; 56 import org.chromium.android_webview.gfx.AwPicture; 57 import org.chromium.android_webview.gfx.RectUtils; 58 import org.chromium.android_webview.permission.AwGeolocationCallback; 59 import org.chromium.android_webview.permission.AwPermissionRequest; 60 import org.chromium.android_webview.renderer_priority.RendererPriority; 61 import org.chromium.base.BuildInfo; 62 import org.chromium.base.Callback; 63 import org.chromium.base.CommandLine; 64 import org.chromium.base.ContextUtils; 65 import org.chromium.base.LocaleUtils; 66 import org.chromium.base.Log; 67 import org.chromium.base.ObserverList; 68 import org.chromium.base.ThreadUtils; 69 import org.chromium.base.TraceEvent; 70 import org.chromium.base.annotations.CalledByNative; 71 import org.chromium.base.annotations.CalledByNativeUnchecked; 72 import org.chromium.base.annotations.JNINamespace; 73 import org.chromium.base.annotations.NativeMethods; 74 import org.chromium.base.metrics.RecordHistogram; 75 import org.chromium.base.metrics.ScopedSysTraceEvent; 76 import org.chromium.base.task.AsyncTask; 77 import org.chromium.base.task.PostTask; 78 import org.chromium.components.autofill.AutofillActionModeCallback; 79 import org.chromium.components.autofill.AutofillProvider; 80 import org.chromium.components.content_capture.ContentCaptureConsumer; 81 import org.chromium.components.embedder_support.util.WebResourceResponseInfo; 82 import org.chromium.components.navigation_interception.InterceptNavigationDelegate; 83 import org.chromium.components.navigation_interception.NavigationParams; 84 import org.chromium.components.url_formatter.UrlFormatter; 85 import org.chromium.content_public.browser.ChildProcessImportance; 86 import org.chromium.content_public.browser.ContentViewStatics; 87 import org.chromium.content_public.browser.GestureListenerManager; 88 import org.chromium.content_public.browser.GestureStateListener; 89 import org.chromium.content_public.browser.ImeAdapter; 90 import org.chromium.content_public.browser.ImeEventObserver; 91 import org.chromium.content_public.browser.JavaScriptCallback; 92 import org.chromium.content_public.browser.JavascriptInjector; 93 import org.chromium.content_public.browser.LoadUrlParams; 94 import org.chromium.content_public.browser.MessagePort; 95 import org.chromium.content_public.browser.NavigationController; 96 import org.chromium.content_public.browser.NavigationHistory; 97 import org.chromium.content_public.browser.RenderFrameHost; 98 import org.chromium.content_public.browser.SelectionClient; 99 import org.chromium.content_public.browser.SelectionPopupController; 100 import org.chromium.content_public.browser.SmartClipProvider; 101 import org.chromium.content_public.browser.UiThreadTaskTraits; 102 import org.chromium.content_public.browser.ViewEventSink; 103 import org.chromium.content_public.browser.WebContents; 104 import org.chromium.content_public.browser.WebContentsAccessibility; 105 import org.chromium.content_public.browser.WebContentsInternals; 106 import org.chromium.content_public.browser.navigation_controller.LoadURLType; 107 import org.chromium.content_public.browser.navigation_controller.UserAgentOverrideOption; 108 import org.chromium.content_public.common.ContentUrlConstants; 109 import org.chromium.content_public.common.Referrer; 110 import org.chromium.content_public.common.UseZoomForDSFPolicy; 111 import org.chromium.device.gamepad.GamepadList; 112 import org.chromium.net.NetworkChangeNotifier; 113 import org.chromium.network.mojom.ReferrerPolicy; 114 import org.chromium.ui.VSyncMonitor; 115 import org.chromium.ui.base.ActivityWindowAndroid; 116 import org.chromium.ui.base.Clipboard; 117 import org.chromium.ui.base.PageTransition; 118 import org.chromium.ui.base.ViewAndroidDelegate; 119 import org.chromium.ui.base.WindowAndroid; 120 import org.chromium.ui.display.DisplayAndroid.DisplayAndroidObserver; 121 import org.chromium.url.GURL; 122 123 import java.io.File; 124 import java.lang.annotation.Annotation; 125 import java.lang.ref.WeakReference; 126 import java.net.MalformedURLException; 127 import java.net.URL; 128 import java.util.ArrayList; 129 import java.util.HashMap; 130 import java.util.List; 131 import java.util.Locale; 132 import java.util.Map; 133 import java.util.WeakHashMap; 134 import java.util.concurrent.Callable; 135 import java.util.regex.Matcher; 136 import java.util.regex.Pattern; 137 138 /** 139 * Exposes the native AwContents class, and together these classes wrap the WebContents 140 * and Browser components that are required to implement Android WebView API. This is the 141 * primary entry point for the WebViewProvider implementation; it holds a 1:1 object 142 * relationship with application WebView instances. 143 * (We define this class independent of the hidden WebViewProvider interfaces, to allow 144 * continuous build & test in the open source SDK-based tree). 145 */ 146 @JNINamespace("android_webview") 147 public class AwContents implements SmartClipProvider { 148 private static final String TAG = "AwContents"; 149 private static final boolean TRACE = false; 150 private static final int NO_WARN = 0; 151 private static final int WARN = 1; 152 private static final String PRODUCT_VERSION = AwContentsStatics.getProductVersion(); 153 154 private static final String WEB_ARCHIVE_EXTENSION = ".mht"; 155 // The request code should be unique per WebView/AwContents object. 156 private static final int PROCESS_TEXT_REQUEST_CODE = 100; 157 158 // Used to avoid enabling zooming in / out if resulting zooming will 159 // produce little visible difference. 160 private static final float ZOOM_CONTROLS_EPSILON = 0.007f; 161 162 private static final double MIN_SCREEN_HEIGHT_PERCENTAGE_FOR_INTERSTITIAL = 0.7; 163 164 private static final String SAMSUNG_WORKAROUND_BASE_URL = "email://"; 165 private static final int SAMSUNG_WORKAROUND_DELAY = 200; 166 167 @VisibleForTesting 168 public static final String DATA_BASE_URL_SCHEME_HISTOGRAM_NAME = 169 "Android.WebView.LoadDataWithBaseUrl.BaseUrl"; 170 171 @VisibleForTesting 172 public static final String LOAD_URL_SCHEME_HISTOGRAM_NAME = "Android.WebView.LoadUrl.UrlScheme"; 173 174 // Permit any number of slashes, since chromium seems to canonicalize bad values. 175 private static final Pattern sFileAndroidAssetPattern = 176 Pattern.compile("^file:/*android_(asset|res).*"); 177 178 // Matches a data URL that (may) have a valid fragment selector, pulling the fragment selector 179 // out into a group. Such a URL must contain a single '#' character and everything after that 180 // must be a valid DOM id. 181 // DOM id grammar: https://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-name 182 private static final Pattern sDataURLWithSelectorPattern = 183 Pattern.compile("^[^#]*(#[A-Za-z][A-Za-z0-9\\-_:.]*)$"); 184 185 private static final HashMap<View, AwContents.AwOnPreDrawListener> sRootViewPreDrawListeners = 186 new HashMap<>(); 187 188 private static class ForceAuxiliaryBitmapRendering { 189 private static final boolean sResult = lazyCheck(); lazyCheck()190 private static boolean lazyCheck() { 191 return !AwContentsJni.get().hasRequiredHardwareExtensions(); 192 } 193 } 194 195 // Used to record the UMA histogram Android.WebView.LoadDataWithBaseUrl.HistoryUrl. Since these 196 // values are persisted to logs, they should never be renumbered or reused. 197 @IntDef({HistoryUrl.EMPTY, HistoryUrl.BASEURL, HistoryUrl.DIFFERENT, HistoryUrl.COUNT}) 198 @interface HistoryUrl { 199 int EMPTY = 0; 200 int BASEURL = 1; 201 int DIFFERENT = 2; 202 int COUNT = 3; 203 } 204 205 // Used to record the UMA histogram Android.WebView.LoadDataWithBaseUrl.UrlScheme. Since these 206 // values are persisted to logs, they should never be renumbered or reused. 207 @VisibleForTesting 208 @IntDef({UrlScheme.EMPTY, UrlScheme.UNKNOWN_SCHEME, UrlScheme.HTTP_SCHEME, 209 UrlScheme.HTTPS_SCHEME, UrlScheme.FILE_SCHEME, UrlScheme.FTP_SCHEME, 210 UrlScheme.DATA_SCHEME, UrlScheme.JAVASCRIPT_SCHEME, UrlScheme.ABOUT_SCHEME, 211 UrlScheme.CHROME_SCHEME, UrlScheme.BLOB_SCHEME, UrlScheme.CONTENT_SCHEME, 212 UrlScheme.INTENT_SCHEME, UrlScheme.FILE_ANDROID_ASSET_SCHEME}) 213 public @interface UrlScheme { 214 int EMPTY = 0; 215 int UNKNOWN_SCHEME = 1; 216 int HTTP_SCHEME = 2; 217 int HTTPS_SCHEME = 3; 218 int FILE_SCHEME = 4; 219 int FTP_SCHEME = 5; 220 int DATA_SCHEME = 6; 221 int JAVASCRIPT_SCHEME = 7; 222 int ABOUT_SCHEME = 8; 223 int CHROME_SCHEME = 9; 224 int BLOB_SCHEME = 10; 225 int CONTENT_SCHEME = 11; 226 int INTENT_SCHEME = 12; 227 int FILE_ANDROID_ASSET_SCHEME = 13; // Covers android_asset and android_res URLs 228 int COUNT = 14; 229 } 230 231 /** 232 * WebKit hit test related data structure. These are used to implement 233 * getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView. 234 * All values should be updated together. The native counterpart is 235 * AwHitTestData. 236 */ 237 public static class HitTestData { 238 // Used in getHitTestResult. 239 public int hitTestResultType; 240 public String hitTestResultExtraData; 241 242 // Used in requestFocusNodeHref (all three) and requestImageRef (only imgSrc). 243 public String href; 244 public String anchorText; 245 public String imgSrc; 246 } 247 248 /** 249 * Interface that consumers of {@link AwContents} must implement to allow the proper 250 * dispatching of view methods through the containing view. 251 */ 252 public interface InternalAccessDelegate extends ViewEventSink.InternalAccessDelegate { 253 /** 254 * @see View#overScrollBy(int, int, int, int, int, int, int, int, boolean); 255 */ overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)256 void overScrollBy(int deltaX, int deltaY, 257 int scrollX, int scrollY, 258 int scrollRangeX, int scrollRangeY, 259 int maxOverScrollX, int maxOverScrollY, 260 boolean isTouchEvent); 261 262 /** 263 * @see View#scrollTo(int, int) 264 */ super_scrollTo(int scrollX, int scrollY)265 void super_scrollTo(int scrollX, int scrollY); 266 267 /** 268 * @see View#setMeasuredDimension(int, int) 269 */ setMeasuredDimension(int measuredWidth, int measuredHeight)270 void setMeasuredDimension(int measuredWidth, int measuredHeight); 271 272 /** 273 * @see View#getScrollBarStyle() 274 */ super_getScrollBarStyle()275 int super_getScrollBarStyle(); 276 277 /** 278 * @see View#startActivityForResult(Intent, int) 279 */ super_startActivityForResult(Intent intent, int requestCode)280 void super_startActivityForResult(Intent intent, int requestCode); 281 282 /** 283 * @see View#onConfigurationChanged(Configuration) 284 */ super_onConfigurationChanged(Configuration newConfig)285 void super_onConfigurationChanged(Configuration newConfig); 286 } 287 288 /** 289 * Factory interface used for constructing functors that the Android framework uses for 290 * calling back into Chromium code to render the the contents of a Chromium frame into 291 * an Android view. 292 */ 293 public interface NativeDrawFunctorFactory { 294 /** 295 * Create a GL functor associated with native context |context|. 296 */ createGLFunctor(long context)297 NativeDrawGLFunctor createGLFunctor(long context); 298 299 /** 300 * Used for draw_fn functor. Only one of these methods need to return non-null. 301 * Prefer this over createGLFunctor. 302 */ getDrawFnAccess()303 AwDrawFnImpl.DrawFnAccess getDrawFnAccess(); 304 } 305 306 /** 307 * Interface that consumers of {@link AwContents} must implement to support 308 * native GL rendering. 309 */ 310 public interface NativeDrawGLFunctor { 311 /** 312 * Requests a callback on the native DrawGL method (see getAwDrawGLFunction). 313 * 314 * If called from within onDraw, |canvas| should be non-null and must be hardware 315 * accelerated. |releasedCallback| should be null if |canvas| is null, or if 316 * supportsDrawGLFunctorReleasedCallback returns false. 317 * 318 * @return false indicates the GL draw request was not accepted, and the caller 319 * should fallback to the SW path. 320 */ requestDrawGL(Canvas canvas, Runnable releasedCallback)321 boolean requestDrawGL(Canvas canvas, Runnable releasedCallback); 322 323 /** 324 * Requests a callback on the native DrawGL method (see getAwDrawGLFunction). 325 * 326 * |containerView| must be hardware accelerated. If |waitForCompletion| is true, this method 327 * will not return until functor has returned. 328 */ requestInvokeGL(View containerView, boolean waitForCompletion)329 boolean requestInvokeGL(View containerView, boolean waitForCompletion); 330 331 /** 332 * Test whether the Android framework supports notifying when a functor is free 333 * to be destroyed via the callback mechanism provided to the functor factory. 334 * 335 * @return true if destruction needs to wait on a framework callback, or false 336 * if it can occur immediately. 337 */ supportsDrawGLFunctorReleasedCallback()338 boolean supportsDrawGLFunctorReleasedCallback(); 339 340 /** 341 * Detaches the GLFunctor from the view tree. 342 */ detach(View containerView)343 void detach(View containerView); 344 345 /** 346 * Destroy this functor instance and any native objects associated with it. No method is 347 * called after destroy. 348 */ destroy()349 void destroy(); 350 } 351 352 /** 353 * Class to facilitate dependency injection. Subclasses by test code to provide mock versions of 354 * certain AwContents dependencies. 355 */ 356 public static class DependencyFactory { createLayoutSizer()357 public AwLayoutSizer createLayoutSizer() { 358 return new AwLayoutSizer(); 359 } 360 createScrollOffsetManager( AwScrollOffsetManager.Delegate delegate)361 public AwScrollOffsetManager createScrollOffsetManager( 362 AwScrollOffsetManager.Delegate delegate) { 363 return new AwScrollOffsetManager(delegate); 364 } 365 createAutofillProvider(Context context, ViewGroup containerView)366 public AutofillProvider createAutofillProvider(Context context, ViewGroup containerView) { 367 return null; 368 } 369 } 370 371 /** 372 * Visual state callback, see {@link #insertVisualStateCallback} for details. 373 * 374 */ 375 @VisibleForTesting 376 public abstract static class VisualStateCallback { 377 /** 378 * @param requestId the id passed to {@link AwContents#insertVisualStateCallback} 379 * which can be used to match requests with the corresponding callbacks. 380 */ onComplete(long requestId)381 public abstract void onComplete(long requestId); 382 } 383 384 private long mNativeAwContents; 385 private AwBrowserContext mBrowserContext; 386 private ViewGroup mContainerView; 387 private AwFunctor mDrawFunctor; 388 private final Context mContext; 389 private final int mAppTargetSdkVersion; 390 private AwViewAndroidDelegate mViewAndroidDelegate; 391 private WindowAndroidWrapper mWindowAndroid; 392 private WebContents mWebContents; 393 private ViewEventSink mViewEventSink; 394 private WebContentsInternalsHolder mWebContentsInternalsHolder; 395 private NavigationController mNavigationController; 396 private final AwContentsClient mContentsClient; 397 private AwWebContentsObserver mWebContentsObserver; 398 private final AwContentsClientBridge mContentsClientBridge; 399 private final AwWebContentsDelegateAdapter mWebContentsDelegate; 400 private final AwContentsBackgroundThreadClient mBackgroundThreadClient; 401 private final AwContentsIoThreadClient mIoThreadClient; 402 private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate; 403 private InternalAccessDelegate mInternalAccessAdapter; 404 private final NativeDrawFunctorFactory mNativeDrawFunctorFactory; 405 private final AwLayoutSizer mLayoutSizer; 406 private final AwZoomControls mZoomControls; 407 private final AwScrollOffsetManager mScrollOffsetManager; 408 private OverScrollGlow mOverScrollGlow; 409 private final DisplayAndroidObserver mDisplayObserver; 410 // This can be accessed on any thread after construction. See AwContentsIoThreadClient. 411 private final AwSettings mSettings; 412 private final ScrollAccessibilityHelper mScrollAccessibilityHelper; 413 414 private final ObserverList<PopupTouchHandleDrawable> mTouchHandleDrawables = 415 new ObserverList<>(); 416 417 private boolean mIsPaused; 418 private boolean mIsViewVisible; 419 private boolean mIsWindowVisible; 420 private boolean mIsAttachedToWindow; 421 // Visiblity state of |mWebContents|. 422 private boolean mIsContentVisible; 423 private boolean mIsUpdateVisibilityTaskPending; 424 private Runnable mUpdateVisibilityRunnable; 425 426 private @RendererPriority int mRendererPriority; 427 private boolean mRendererPriorityWaivedWhenNotVisible; 428 429 private Bitmap mFavicon; 430 private boolean mHasRequestedVisitedHistoryFromClient; 431 // Whether this WebView is a popup. 432 private boolean mIsPopupWindow; 433 434 // The base background color, i.e. not accounting for any CSS body from the current page. 435 private int mBaseBackgroundColor = Color.WHITE; 436 437 // Did background set by developer, now used for dark mode. 438 private boolean mDidInitBackground; 439 440 // Must call AwContentsJni.get().updateLastHitTestData first to update this before use. 441 private final HitTestData mPossiblyStaleHitTestData = new HitTestData(); 442 443 private final DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler; 444 445 // Bound method for suppling Picture instances to the AwContentsClient. Will be null if the 446 // picture listener API has not yet been enabled, or if it is using invalidation-only mode. 447 private Callable<Picture> mPictureListenerContentProvider; 448 449 private boolean mContainerViewFocused; 450 private boolean mWindowFocused; 451 452 // These come from the compositor and are updated synchronously (in contrast to the values in 453 // RenderCoordinates, which are updated at end of every frame). 454 private float mPageScaleFactor = 1.0f; 455 private float mMinPageScaleFactor = 1.0f; 456 private float mMaxPageScaleFactor = 1.0f; 457 private float mContentWidthDip; 458 private float mContentHeightDip; 459 460 private AwAutofillClient mAwAutofillClient; 461 462 private AwPdfExporter mAwPdfExporter; 463 464 private AwViewMethods mAwViewMethods; 465 private final FullScreenTransitionsState mFullScreenTransitionsState; 466 467 // This is a workaround for some qualcomm devices discarding buffer on 468 // Activity restore. 469 private boolean mInvalidateRootViewOnNextDraw; 470 471 // The framework may temporarily detach our container view, for example during layout if 472 // we are a child of a ListView. This may cause many toggles of View focus, which we suppress 473 // when in this state. 474 private boolean mTemporarilyDetached; 475 476 // True when this AwContents has been destroyed. 477 // Do not use directly, call isDestroyed() instead. 478 private boolean mIsDestroyed; 479 480 private AutofillProvider mAutofillProvider; 481 482 private static String sCurrentLocales = ""; 483 484 private Paint mPaintForNWorkaround; 485 486 // A holder of objects passed from WebContents and should be owned by AwContents that may 487 // have direct or indirect reference back to WebView. They are used internally by 488 // WebContents but all the references can create a new gc root that can keep WebView 489 // instances from being freed when they are detached from view tree, hence lead to 490 // memory leak. To avoid the issue, it is possible to use |WebContents.setInternalHolder| 491 // to move the holder of those internal objects to AwContents. Note that they are still 492 // used by WebContents, and AwContents doesn't have to know what's inside the holder. 493 private WebContentsInternals mWebContentsInternals; 494 495 private JavascriptInjector mJavascriptInjector; 496 497 private ContentCaptureConsumer mContentCaptureConsumer; 498 499 private static class WebContentsInternalsHolder implements WebContents.InternalsHolder { 500 private final WeakReference<AwContents> mAwContentsRef; 501 WebContentsInternalsHolder(AwContents awContents)502 private WebContentsInternalsHolder(AwContents awContents) { 503 mAwContentsRef = new WeakReference<>(awContents); 504 } 505 506 @Override set(WebContentsInternals internals)507 public void set(WebContentsInternals internals) { 508 AwContents awContents = mAwContentsRef.get(); 509 if (awContents == null) { 510 throw new IllegalStateException("AwContents should be available at this time"); 511 } 512 awContents.mWebContentsInternals = internals; 513 } 514 515 @Override get()516 public WebContentsInternals get() { 517 AwContents awContents = mAwContentsRef.get(); 518 return awContents == null ? null : awContents.mWebContentsInternals; 519 } 520 weakRefCleared()521 public boolean weakRefCleared() { 522 return mAwContentsRef.get() == null; 523 } 524 } 525 526 private static final class AwContentsDestroyRunnable implements Runnable { 527 private final long mNativeAwContents; 528 // Hold onto a reference to the window (via its wrapper), so that it is not destroyed 529 // until we are done here. 530 private final WindowAndroidWrapper mWindowAndroid; 531 AwContentsDestroyRunnable( long nativeAwContents, WindowAndroidWrapper windowAndroid)532 private AwContentsDestroyRunnable( 533 long nativeAwContents, WindowAndroidWrapper windowAndroid) { 534 mNativeAwContents = nativeAwContents; 535 mWindowAndroid = windowAndroid; 536 mWindowAndroid.incrementRefFromDestroyRunnable(); 537 } 538 539 @Override run()540 public void run() { 541 AwContentsJni.get().destroy(mNativeAwContents); 542 mWindowAndroid.decrementRefFromDestroyRunnable(); 543 } 544 } 545 546 /** 547 * A class that stores the state needed to enter and exit fullscreen. 548 */ 549 private static class FullScreenTransitionsState { 550 private final ViewGroup mInitialContainerView; 551 private final InternalAccessDelegate mInitialInternalAccessAdapter; 552 private final AwViewMethods mInitialAwViewMethods; 553 private FullScreenView mFullScreenView; 554 /** Whether the initial container view was focused when we entered fullscreen */ 555 private boolean mWasInitialContainerViewFocused; 556 private int mScrollX; 557 private int mScrollY; 558 FullScreenTransitionsState(ViewGroup initialContainerView, InternalAccessDelegate initialInternalAccessAdapter, AwViewMethods initialAwViewMethods)559 private FullScreenTransitionsState(ViewGroup initialContainerView, 560 InternalAccessDelegate initialInternalAccessAdapter, 561 AwViewMethods initialAwViewMethods) { 562 mInitialContainerView = initialContainerView; 563 mInitialInternalAccessAdapter = initialInternalAccessAdapter; 564 mInitialAwViewMethods = initialAwViewMethods; 565 } 566 enterFullScreen(FullScreenView fullScreenView, boolean wasInitialContainerViewFocused, int scrollX, int scrollY)567 private void enterFullScreen(FullScreenView fullScreenView, 568 boolean wasInitialContainerViewFocused, int scrollX, int scrollY) { 569 mFullScreenView = fullScreenView; 570 mWasInitialContainerViewFocused = wasInitialContainerViewFocused; 571 mScrollX = scrollX; 572 mScrollY = scrollY; 573 } 574 wasInitialContainerViewFocused()575 private boolean wasInitialContainerViewFocused() { 576 return mWasInitialContainerViewFocused; 577 } 578 getScrollX()579 private int getScrollX() { 580 return mScrollX; 581 } 582 getScrollY()583 private int getScrollY() { 584 return mScrollY; 585 } 586 exitFullScreen()587 private void exitFullScreen() { 588 mFullScreenView = null; 589 } 590 isFullScreen()591 private boolean isFullScreen() { 592 return mFullScreenView != null; 593 } 594 getInitialContainerView()595 private ViewGroup getInitialContainerView() { 596 return mInitialContainerView; 597 } 598 getInitialInternalAccessDelegate()599 private InternalAccessDelegate getInitialInternalAccessDelegate() { 600 return mInitialInternalAccessAdapter; 601 } 602 getInitialAwViewMethods()603 private AwViewMethods getInitialAwViewMethods() { 604 return mInitialAwViewMethods; 605 } 606 getFullScreenView()607 private FullScreenView getFullScreenView() { 608 return mFullScreenView; 609 } 610 } 611 612 // Reference to the active mNativeAwContents pointer while it is active use 613 // (ie before it is destroyed). 614 private CleanupReference mCleanupReference; 615 616 //-------------------------------------------------------------------------------------------- 617 private class IoThreadClientImpl extends AwContentsIoThreadClient { 618 // All methods are called on the IO thread. 619 620 @Override getCacheMode()621 public int getCacheMode() { 622 return mSettings.getCacheMode(); 623 } 624 625 @Override getBackgroundThreadClient()626 public AwContentsBackgroundThreadClient getBackgroundThreadClient() { 627 return mBackgroundThreadClient; 628 } 629 630 @Override shouldBlockContentUrls()631 public boolean shouldBlockContentUrls() { 632 return !mSettings.getAllowContentAccess(); 633 } 634 635 @Override shouldBlockFileUrls()636 public boolean shouldBlockFileUrls() { 637 return !mSettings.getAllowFileAccess(); 638 } 639 640 @Override shouldBlockNetworkLoads()641 public boolean shouldBlockNetworkLoads() { 642 return mSettings.getBlockNetworkLoads(); 643 } 644 645 @Override shouldAcceptThirdPartyCookies()646 public boolean shouldAcceptThirdPartyCookies() { 647 return mSettings.getAcceptThirdPartyCookies(); 648 } 649 650 @Override getSafeBrowsingEnabled()651 public boolean getSafeBrowsingEnabled() { 652 return mSettings.getSafeBrowsingEnabled(); 653 } 654 } 655 656 private class BackgroundThreadClientImpl extends AwContentsBackgroundThreadClient { 657 // All methods are called on the background thread. 658 659 @Override shouldInterceptRequest( AwContentsClient.AwWebResourceRequest request)660 public WebResourceResponseInfo shouldInterceptRequest( 661 AwContentsClient.AwWebResourceRequest request) { 662 String url = request.url; 663 WebResourceResponseInfo webResourceResponseInfo; 664 // Return the response directly if the url is default video poster url. 665 webResourceResponseInfo = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url); 666 if (webResourceResponseInfo != null) return webResourceResponseInfo; 667 668 webResourceResponseInfo = mContentsClient.shouldInterceptRequest(request); 669 670 if (webResourceResponseInfo == null) { 671 mContentsClient.getCallbackHelper().postOnLoadResource(url); 672 } 673 674 if (webResourceResponseInfo != null && webResourceResponseInfo.getData() == null) { 675 // In this case the intercepted URLRequest job will simulate an empty response 676 // which doesn't trigger the onReceivedError callback. For WebViewClassic 677 // compatibility we synthesize that callback. http://crbug.com/180950 678 mContentsClient.getCallbackHelper().postOnReceivedError( 679 request, 680 /* error description filled in by the glue layer */ 681 new AwContentsClient.AwWebResourceError()); 682 } 683 return webResourceResponseInfo; 684 } 685 } 686 687 //-------------------------------------------------------------------------------------------- 688 // When the navigation is for a newly created WebView (i.e. a popup), intercept the navigation 689 // here for implementing shouldOverrideUrlLoading. This is to send the shouldOverrideUrlLoading 690 // callback to the correct WebViewClient that is associated with the WebView. 691 // Otherwise, use this delegate only to post onPageStarted messages. 692 // 693 // We are not using WebContentsObserver.didStartLoading because of stale URLs, out of order 694 // onPageStarted's and double onPageStarted's. 695 // 696 private class InterceptNavigationDelegateImpl implements InterceptNavigationDelegate { 697 @Override shouldIgnoreNavigation(NavigationParams navigationParams)698 public boolean shouldIgnoreNavigation(NavigationParams navigationParams) { 699 // The shouldOverrideUrlLoading call might have resulted in posting messages to the 700 // UI thread. Using sendMessage here (instead of calling onPageStarted directly) 701 // will allow those to run in order. 702 if (!AwFeatureList.pageStartedOnCommitEnabled(navigationParams.isRendererInitiated)) { 703 mContentsClient.getCallbackHelper().postOnPageStarted(navigationParams.url); 704 } 705 return false; 706 } 707 } 708 709 //-------------------------------------------------------------------------------------------- 710 private class AwLayoutSizerDelegate implements AwLayoutSizer.Delegate { 711 @Override requestLayout()712 public void requestLayout() { 713 mContainerView.requestLayout(); 714 } 715 716 @Override setMeasuredDimension(int measuredWidth, int measuredHeight)717 public void setMeasuredDimension(int measuredWidth, int measuredHeight) { 718 mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight); 719 } 720 721 @Override isLayoutParamsHeightWrapContent()722 public boolean isLayoutParamsHeightWrapContent() { 723 return mContainerView.getLayoutParams() != null 724 && (mContainerView.getLayoutParams().height 725 == ViewGroup.LayoutParams.WRAP_CONTENT); 726 } 727 728 @Override setForceZeroLayoutHeight(boolean forceZeroHeight)729 public void setForceZeroLayoutHeight(boolean forceZeroHeight) { 730 getSettings().setForceZeroLayoutHeight(forceZeroHeight); 731 } 732 } 733 734 //-------------------------------------------------------------------------------------------- 735 private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate { 736 @Override overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, boolean isTouchEvent)737 public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, 738 int scrollRangeX, int scrollRangeY, boolean isTouchEvent) { 739 mInternalAccessAdapter.overScrollBy(deltaX, deltaY, scrollX, scrollY, 740 scrollRangeX, scrollRangeY, 0, 0, isTouchEvent); 741 } 742 743 @Override scrollContainerViewTo(int x, int y)744 public void scrollContainerViewTo(int x, int y) { 745 try { 746 mInternalAccessAdapter.super_scrollTo(x, y); 747 } catch (Throwable e) { 748 AwThreadUtils.postToCurrentLooper(() -> { 749 Log.e(TAG, "The following exception was raised by scrollContainerViewTo:"); 750 throw e; 751 }); 752 } 753 } 754 755 @Override scrollNativeTo(int x, int y)756 public void scrollNativeTo(int x, int y) { 757 if (!isDestroyed(NO_WARN)) { 758 AwContentsJni.get().scrollTo(mNativeAwContents, AwContents.this, x, y); 759 } 760 } 761 762 @Override smoothScroll(int targetX, int targetY, long durationMs)763 public void smoothScroll(int targetX, int targetY, long durationMs) { 764 if (!isDestroyed(NO_WARN)) { 765 AwContentsJni.get().smoothScroll( 766 mNativeAwContents, AwContents.this, targetX, targetY, durationMs); 767 } 768 } 769 770 @Override getContainerViewScrollX()771 public int getContainerViewScrollX() { 772 return mContainerView.getScrollX(); 773 } 774 775 @Override getContainerViewScrollY()776 public int getContainerViewScrollY() { 777 return mContainerView.getScrollY(); 778 } 779 780 @Override invalidate()781 public void invalidate() { 782 postInvalidateOnAnimation(); 783 } 784 785 @Override cancelFling()786 public void cancelFling() { 787 mWebContents.getEventForwarder().cancelFling(SystemClock.uptimeMillis()); 788 } 789 } 790 791 //-------------------------------------------------------------------------------------------- 792 private class AwGestureStateListener implements GestureStateListener { 793 @Override onPinchStarted()794 public void onPinchStarted() { 795 // While it's possible to re-layout the view during a pinch gesture, the effect is very 796 // janky (especially that the page scale update notification comes from the renderer 797 // main thread, not from the impl thread, so it's usually out of sync with what's on 798 // screen). It's also quite expensive to do a re-layout, so we simply postpone 799 // re-layout for the duration of the gesture. This is compatible with what 800 // WebViewClassic does. 801 mLayoutSizer.freezeLayoutRequests(); 802 } 803 804 @Override onPinchEnded()805 public void onPinchEnded() { 806 mLayoutSizer.unfreezeLayoutRequests(); 807 } 808 809 @Override onScrollUpdateGestureConsumed()810 public void onScrollUpdateGestureConsumed() { 811 mScrollAccessibilityHelper.postViewScrolledAccessibilityEventCallback(); 812 mZoomControls.invokeZoomPicker(); 813 } 814 815 @Override onScrollStarted(int scrollOffsetY, int scrollExtentY)816 public void onScrollStarted(int scrollOffsetY, int scrollExtentY) { 817 mZoomControls.invokeZoomPicker(); 818 } 819 820 @Override onScaleLimitsChanged(float minPageScaleFactor, float maxPageScaleFactor)821 public void onScaleLimitsChanged(float minPageScaleFactor, float maxPageScaleFactor) { 822 mZoomControls.updateZoomControls(); 823 } 824 } 825 826 //-------------------------------------------------------------------------------------------- 827 private class AwComponentCallbacks implements ComponentCallbacks2 { 828 @Override onTrimMemory(final int level)829 public void onTrimMemory(final int level) { 830 boolean visibleRectEmpty = getGlobalVisibleRect().isEmpty(); 831 final boolean visible = mIsViewVisible && mIsWindowVisible && !visibleRectEmpty; 832 ThreadUtils.runOnUiThreadBlocking(() -> { 833 if (isDestroyed(NO_WARN)) return; 834 if (level >= TRIM_MEMORY_MODERATE) { 835 if (mDrawFunctor != null) { 836 mDrawFunctor.trimMemory(); 837 } 838 } 839 AwContentsJni.get().trimMemory(mNativeAwContents, AwContents.this, level, visible); 840 }); 841 } 842 843 @Override onLowMemory()844 public void onLowMemory() {} 845 846 @Override onConfigurationChanged(Configuration configuration)847 public void onConfigurationChanged(Configuration configuration) { 848 updateDefaultLocale(); 849 } 850 }; 851 852 //-------------------------------------------------------------------------------------------- 853 private class AwDisplayAndroidObserver implements DisplayAndroidObserver { 854 @Override onRotationChanged(int rotation)855 public void onRotationChanged(int rotation) {} 856 857 @Override onDIPScaleChanged(float dipScale)858 public void onDIPScaleChanged(float dipScale) { 859 if (TRACE) Log.i(TAG, "%s onDIPScaleChanged dipScale=%f", this, dipScale); 860 861 AwContentsJni.get().setDipScale(mNativeAwContents, AwContents.this, dipScale); 862 mLayoutSizer.setDIPScale(dipScale); 863 mSettings.setDIPScale(dipScale); 864 } 865 }; 866 867 private static class AwOnPreDrawListener implements ViewTreeObserver.OnPreDrawListener { 868 private View mRootView; 869 private List<AwContents> mAwContentsList = new ArrayList<AwContents>(); 870 AwOnPreDrawListener(View rootView)871 public AwOnPreDrawListener(View rootView) { 872 mRootView = rootView; 873 } 874 trackContents(AwContents contents)875 public boolean trackContents(AwContents contents) { 876 return mAwContentsList.add(contents); 877 } 878 unTrackContents(AwContents contents)879 public boolean unTrackContents(AwContents contents) { 880 return mAwContentsList.remove(contents); 881 } 882 isTracking()883 public boolean isTracking() { 884 return mAwContentsList.size() > 0; 885 } 886 887 @Override onPreDraw()888 public boolean onPreDraw() { 889 if (TRACE) Log.i(TAG, "%s onPreDraw", this); 890 List<Rect> openWebContentRects = new ArrayList<Rect>(); 891 for (AwContents content : mAwContentsList) { 892 assert !content.isDestroyed(NO_WARN); 893 if (AwContentsJni.get().isDisplayingOpenWebContent( 894 content.mNativeAwContents, content)) { 895 openWebContentRects.add(content.getGlobalVisibleRect()); 896 } 897 } 898 899 Rect rootVisibleRect = new Rect((int) mRootView.getX(), (int) mRootView.getY(), 900 (int) mRootView.getX() + mRootView.getWidth(), 901 (int) mRootView.getY() + mRootView.getHeight()); 902 int openWebPixelCoverage = 903 RectUtils.calculatePixelsOfCoverage(rootVisibleRect, openWebContentRects); 904 905 float openWebVisiblePercentage = 906 (float) openWebPixelCoverage / RectUtils.getRectArea(rootVisibleRect); 907 908 AwContentsJni.get().updateOpenWebScreenArea( 909 openWebPixelCoverage, (int) openWebVisiblePercentage); 910 return true; 911 } 912 } 913 914 //-------------------------------------------------------------------------------------------- 915 /** 916 * @param browserContext the browsing context to associate this view contents with. 917 * @param containerView the view-hierarchy item this object will be bound to. 918 * @param context the context to use, usually containerView.getContext(). 919 * @param internalAccessAdapter to access private methods on containerView. 920 * @param nativeDrawFunctorFactory to access the functor provided by the WebView. 921 * @param contentsClient will receive API callbacks from this WebView Contents. 922 * @param awSettings AwSettings instance used to configure the AwContents. 923 * 924 * This constructor uses the default view sizing policy. 925 */ AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, InternalAccessDelegate internalAccessAdapter, NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient, AwSettings awSettings)926 public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, 927 InternalAccessDelegate internalAccessAdapter, 928 NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient, 929 AwSettings awSettings) { 930 this(browserContext, containerView, context, internalAccessAdapter, 931 nativeDrawFunctorFactory, contentsClient, awSettings, new DependencyFactory()); 932 } 933 934 /** 935 * @param dependencyFactory an instance of the DependencyFactory used to provide instances of 936 * classes that this class depends on. 937 * 938 * This version of the constructor is used in test code to inject test versions of the above 939 * documented classes. 940 */ AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, InternalAccessDelegate internalAccessAdapter, NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient, AwSettings settings, DependencyFactory dependencyFactory)941 public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Context context, 942 InternalAccessDelegate internalAccessAdapter, 943 NativeDrawFunctorFactory nativeDrawFunctorFactory, AwContentsClient contentsClient, 944 AwSettings settings, DependencyFactory dependencyFactory) { 945 assert browserContext != null; 946 try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped("AwContents.constructor")) { 947 mRendererPriority = RendererPriority.HIGH; 948 mSettings = settings; 949 updateDefaultLocale(); 950 951 mBrowserContext = browserContext; 952 953 // setWillNotDraw(false) is required since WebView draws its own contents using its 954 // container view. If this is ever not the case we should remove this, as it removes 955 // Android's gatherTransparentRegion optimization for the view. 956 mContainerView = containerView; 957 mContainerView.setWillNotDraw(false); 958 959 mContext = context; 960 mAutofillProvider = dependencyFactory.createAutofillProvider(context, mContainerView); 961 mAppTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; 962 mInternalAccessAdapter = internalAccessAdapter; 963 mNativeDrawFunctorFactory = nativeDrawFunctorFactory; 964 mContentsClient = contentsClient; 965 mContentsClient.getCallbackHelper().setCancelCallbackPoller( 966 () -> AwContents.this.isDestroyed(NO_WARN)); 967 mAwViewMethods = new AwViewMethodsImpl(); 968 mFullScreenTransitionsState = new FullScreenTransitionsState( 969 mContainerView, mInternalAccessAdapter, mAwViewMethods); 970 mLayoutSizer = dependencyFactory.createLayoutSizer(); 971 mLayoutSizer.setDelegate(new AwLayoutSizerDelegate()); 972 mWebContentsDelegate = new AwWebContentsDelegateAdapter( 973 this, contentsClient, settings, mContext, mContainerView); 974 mContentsClientBridge = new AwContentsClientBridge( 975 mContext, contentsClient, AwContentsStatics.getClientCertLookupTable()); 976 mZoomControls = new AwZoomControls(this); 977 mBackgroundThreadClient = new BackgroundThreadClientImpl(); 978 mIoThreadClient = new IoThreadClientImpl(); 979 mInterceptNavigationDelegate = new InterceptNavigationDelegateImpl(); 980 mDisplayObserver = new AwDisplayAndroidObserver(); 981 mUpdateVisibilityRunnable = () -> updateWebContentsVisibility(); 982 983 AwSettings.ZoomSupportChangeListener zoomListener = 984 (supportsDoubleTapZoom, supportsMultiTouchZoom) -> { 985 if (isDestroyed(NO_WARN)) return; 986 GestureListenerManager gestureManager = 987 GestureListenerManager.fromWebContents(mWebContents); 988 gestureManager.updateDoubleTapSupport(supportsDoubleTapZoom); 989 gestureManager.updateMultiTouchZoomSupport(supportsMultiTouchZoom); 990 }; 991 mSettings.setZoomListener(zoomListener); 992 mDefaultVideoPosterRequestHandler = 993 new DefaultVideoPosterRequestHandler(mContentsClient); 994 mSettings.setDefaultVideoPosterURL( 995 mDefaultVideoPosterRequestHandler.getDefaultVideoPosterURL()); 996 mScrollOffsetManager = dependencyFactory.createScrollOffsetManager( 997 new AwScrollOffsetManagerDelegate()); 998 mScrollAccessibilityHelper = new ScrollAccessibilityHelper(mContainerView); 999 1000 setOverScrollMode(mContainerView.getOverScrollMode()); 1001 setScrollBarStyle(mInternalAccessAdapter.super_getScrollBarStyle()); 1002 1003 setNewAwContents(AwContentsJni.get().init(mBrowserContext.getNativePointer())); 1004 1005 onContainerViewChanged(); 1006 } 1007 } 1008 initWebContents(ViewAndroidDelegate viewDelegate, InternalAccessDelegate internalDispatcher, WebContents webContents, WindowAndroid windowAndroid, WebContentsInternalsHolder internalsHolder)1009 private void initWebContents(ViewAndroidDelegate viewDelegate, 1010 InternalAccessDelegate internalDispatcher, WebContents webContents, 1011 WindowAndroid windowAndroid, WebContentsInternalsHolder internalsHolder) { 1012 webContents.initialize( 1013 PRODUCT_VERSION, viewDelegate, internalDispatcher, windowAndroid, internalsHolder); 1014 mViewEventSink = ViewEventSink.from(mWebContents); 1015 mViewEventSink.setHideKeyboardOnBlur(false); 1016 SelectionPopupController controller = SelectionPopupController.fromWebContents(webContents); 1017 controller.setActionModeCallback(new AwActionModeCallback(mContext, this, webContents)); 1018 if (mAutofillProvider != null) { 1019 controller.setNonSelectionActionModeCallback( 1020 new AutofillActionModeCallback(mContext, mAutofillProvider)); 1021 } 1022 controller.setSelectionClient(SelectionClient.createSmartSelectionClient(webContents)); 1023 1024 // Listen for dpad events from IMEs (e.g. Samsung Cursor Control) so we know to enable 1025 // spatial navigation mode to allow these events to move focus out of the WebView. 1026 ImeAdapter.fromWebContents(webContents).addEventObserver(new ImeEventObserver() { 1027 @Override 1028 public void onBeforeSendKeyEvent(KeyEvent event) { 1029 if (AwContents.isDpadEvent(event)) { 1030 mSettings.setSpatialNavigationEnabled(true); 1031 } 1032 } 1033 }); 1034 } 1035 isSamsungMailApp()1036 private boolean isSamsungMailApp() { 1037 // There are 2 different Samsung mail apps exhibiting bugs related to 1038 // http://crbug.com/781535. 1039 String currentPackageName = mContext.getPackageName(); 1040 return "com.android.email".equals(currentPackageName) 1041 || "com.samsung.android.email.composer".equals(currentPackageName); 1042 } 1043 isFullScreen()1044 boolean isFullScreen() { 1045 return mFullScreenTransitionsState.isFullScreen(); 1046 } 1047 1048 /** 1049 * Transitions this {@link AwContents} to fullscreen mode and returns the 1050 * {@link View} where the contents will be drawn while in fullscreen, or null 1051 * if this AwContents has already been destroyed. 1052 */ enterFullScreen()1053 View enterFullScreen() { 1054 assert !isFullScreen(); 1055 if (isDestroyed(NO_WARN)) return null; 1056 1057 // Detach to tear down the GL functor if this is still associated with the old 1058 // container view. It will be recreated during the next call to onDraw attached to 1059 // the new container view. 1060 onDetachedFromWindow(); 1061 1062 // In fullscreen mode FullScreenView owns the AwViewMethodsImpl and AwContents 1063 // a NullAwViewMethods. 1064 FullScreenView fullScreenView = new FullScreenView(mContext, mAwViewMethods, this); 1065 fullScreenView.setFocusable(true); 1066 fullScreenView.setFocusableInTouchMode(true); 1067 boolean wasInitialContainerViewFocused = mContainerView.isFocused(); 1068 if (wasInitialContainerViewFocused) { 1069 fullScreenView.requestFocus(); 1070 } 1071 mFullScreenTransitionsState.enterFullScreen(fullScreenView, wasInitialContainerViewFocused, 1072 mScrollOffsetManager.getScrollX(), mScrollOffsetManager.getScrollY()); 1073 mAwViewMethods = new NullAwViewMethods(this, mInternalAccessAdapter, mContainerView); 1074 1075 // Associate this AwContents with the FullScreenView. 1076 setInternalAccessAdapter(fullScreenView.getInternalAccessAdapter()); 1077 setContainerView(fullScreenView); 1078 1079 return fullScreenView; 1080 } 1081 1082 /** 1083 * Called when the app has requested to exit fullscreen. 1084 */ requestExitFullscreen()1085 void requestExitFullscreen() { 1086 if (!isDestroyed(NO_WARN)) mWebContents.exitFullscreen(); 1087 } 1088 1089 /** 1090 * Returns this {@link AwContents} to embedded mode, where the {@link AwContents} are drawn 1091 * in the WebView. 1092 */ exitFullScreen()1093 void exitFullScreen() { 1094 if (!isFullScreen() || isDestroyed(NO_WARN)) { 1095 // exitFullScreen() can be called without a prior call to enterFullScreen() if a 1096 // "misbehave" app overrides onShowCustomView but does not add the custom view to 1097 // the window. Exiting avoids a crash. 1098 return; 1099 } 1100 1101 // Detach to tear down the GL functor if this is still associated with the old 1102 // container view. It will be recreated during the next call to onDraw attached to 1103 // the new container view. 1104 // NOTE: we cannot use mAwViewMethods here because its type is NullAwViewMethods. 1105 AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods(); 1106 awViewMethodsImpl.onDetachedFromWindow(); 1107 1108 // Swap the view delegates. In embedded mode the FullScreenView owns a 1109 // NullAwViewMethods and AwContents the AwViewMethodsImpl. 1110 FullScreenView fullscreenView = mFullScreenTransitionsState.getFullScreenView(); 1111 fullscreenView.setAwViewMethods(new NullAwViewMethods( 1112 this, fullscreenView.getInternalAccessAdapter(), fullscreenView)); 1113 mAwViewMethods = awViewMethodsImpl; 1114 ViewGroup initialContainerView = mFullScreenTransitionsState.getInitialContainerView(); 1115 1116 // Re-associate this AwContents with the WebView. 1117 setInternalAccessAdapter(mFullScreenTransitionsState.getInitialInternalAccessDelegate()); 1118 setContainerView(initialContainerView); 1119 1120 // Return focus to the WebView. 1121 if (mFullScreenTransitionsState.wasInitialContainerViewFocused()) { 1122 mContainerView.requestFocus(); 1123 } 1124 1125 if (!isDestroyed(NO_WARN)) { 1126 AwContentsJni.get().restoreScrollAfterTransition(mNativeAwContents, AwContents.this, 1127 mFullScreenTransitionsState.getScrollX(), 1128 mFullScreenTransitionsState.getScrollY()); 1129 } 1130 1131 mFullScreenTransitionsState.exitFullScreen(); 1132 } 1133 setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter)1134 private void setInternalAccessAdapter(InternalAccessDelegate internalAccessAdapter) { 1135 mInternalAccessAdapter = internalAccessAdapter; 1136 mViewEventSink.setAccessDelegate(mInternalAccessAdapter); 1137 } 1138 setContainerView(ViewGroup newContainerView)1139 private void setContainerView(ViewGroup newContainerView) { 1140 // setWillNotDraw(false) is required since WebView draws its own contents using its 1141 // container view. If this is ever not the case we should remove this, as it removes 1142 // Android's gatherTransparentRegion optimization for the view. 1143 mContainerView = newContainerView; 1144 mContainerView.setWillNotDraw(false); 1145 1146 assert mDrawFunctor == null; 1147 1148 mViewAndroidDelegate.setContainerView(mContainerView); 1149 if (mAwPdfExporter != null) { 1150 mAwPdfExporter.setContainerView(mContainerView); 1151 } 1152 mWebContentsDelegate.setContainerView(mContainerView); 1153 for (PopupTouchHandleDrawable drawable: mTouchHandleDrawables) { 1154 drawable.onContainerViewChanged(newContainerView); 1155 } 1156 onContainerViewChanged(); 1157 } 1158 1159 /** 1160 * Reconciles the state of this AwContents object with the state of the new container view. 1161 */ 1162 @SuppressLint("NewApi") // ViewGroup#isAttachedToWindow requires API level 19. onContainerViewChanged()1163 private void onContainerViewChanged() { 1164 // NOTE: mAwViewMethods is used by the old container view, the WebView, so it might refer 1165 // to a NullAwViewMethods when in fullscreen. To ensure that the state is reconciled with 1166 // the new container view correctly, we bypass mAwViewMethods and use the real 1167 // implementation directly. 1168 AwViewMethods awViewMethodsImpl = mFullScreenTransitionsState.getInitialAwViewMethods(); 1169 awViewMethodsImpl.onVisibilityChanged(mContainerView, mContainerView.getVisibility()); 1170 awViewMethodsImpl.onWindowVisibilityChanged(mContainerView.getWindowVisibility()); 1171 1172 boolean containerViewAttached = mContainerView.isAttachedToWindow(); 1173 if (containerViewAttached && !mIsAttachedToWindow) { 1174 awViewMethodsImpl.onAttachedToWindow(); 1175 } else if (!containerViewAttached && mIsAttachedToWindow) { 1176 awViewMethodsImpl.onDetachedFromWindow(); 1177 } 1178 // Skip passing size of FullScreenView down. FullScreenView is newly created and detached 1179 // so has initial size 0x0 before layout. Avoid this temporary resize to 0x0 which can 1180 // cause flickers and sometimes layout problems in the web page. 1181 if ((mContainerView instanceof FullScreenView)) { 1182 assert !containerViewAttached; 1183 } else { 1184 awViewMethodsImpl.onSizeChanged( 1185 mContainerView.getWidth(), mContainerView.getHeight(), 0, 0); 1186 } 1187 awViewMethodsImpl.onWindowFocusChanged(mContainerView.hasWindowFocus()); 1188 awViewMethodsImpl.onFocusChanged(mContainerView.hasFocus(), 0, null); 1189 mContainerView.requestLayout(); 1190 if (mAutofillProvider != null) mAutofillProvider.onContainerViewChanged(mContainerView); 1191 } 1192 1193 // This class destroys the WindowAndroid when after it is gc-ed. 1194 private static class WindowAndroidWrapper { 1195 private final WindowAndroid mWindowAndroid; 1196 private final CleanupReference mCleanupReference; 1197 1198 // This ref-counts is used only to destroy WindowAndroid eagerly 1199 // when AwContents is destroyed. The CleanupReference is still used 1200 // if a Wrapper is created without any AwContents. 1201 private int mRefFromAwContentsDestroyRunnable; 1202 1203 private static final class DestroyRunnable implements Runnable { 1204 private final WindowAndroid mWindowAndroid; DestroyRunnable(WindowAndroid windowAndroid)1205 private DestroyRunnable(WindowAndroid windowAndroid) { 1206 mWindowAndroid = windowAndroid; 1207 } 1208 @Override run()1209 public void run() { 1210 mWindowAndroid.destroy(); 1211 } 1212 } 1213 WindowAndroidWrapper(WindowAndroid windowAndroid)1214 public WindowAndroidWrapper(WindowAndroid windowAndroid) { 1215 try (ScopedSysTraceEvent e = 1216 ScopedSysTraceEvent.scoped("WindowAndroidWrapper.constructor")) { 1217 mWindowAndroid = windowAndroid; 1218 mCleanupReference = new CleanupReference(this, new DestroyRunnable(windowAndroid)); 1219 } 1220 } 1221 getWindowAndroid()1222 public WindowAndroid getWindowAndroid() { 1223 return mWindowAndroid; 1224 } 1225 incrementRefFromDestroyRunnable()1226 public void incrementRefFromDestroyRunnable() { 1227 mRefFromAwContentsDestroyRunnable++; 1228 } 1229 decrementRefFromDestroyRunnable()1230 public void decrementRefFromDestroyRunnable() { 1231 assert mRefFromAwContentsDestroyRunnable > 0; 1232 mRefFromAwContentsDestroyRunnable--; 1233 maybeCleanupEarly(); 1234 } 1235 maybeCleanupEarly()1236 private void maybeCleanupEarly() { 1237 if (mRefFromAwContentsDestroyRunnable != 0) return; 1238 1239 Context context = mWindowAndroid.getContext().get(); 1240 if (context != null && sContextWindowMap.get(context) != this) return; 1241 1242 mCleanupReference.cleanupNow(); 1243 if (context != null) sContextWindowMap.remove(context); 1244 } 1245 } 1246 private static WeakHashMap<Context, WindowAndroidWrapper> sContextWindowMap; 1247 1248 // getWindowAndroid is only called on UI thread, so there are no threading issues with lazy 1249 // initialization. getWindowAndroid(Context context)1250 private static WindowAndroidWrapper getWindowAndroid(Context context) { 1251 if (sContextWindowMap == null) sContextWindowMap = new WeakHashMap<>(); 1252 WindowAndroidWrapper wrapper = sContextWindowMap.get(context); 1253 if (wrapper != null) return wrapper; 1254 1255 try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped("AwContents.getWindowAndroid")) { 1256 boolean contextWrapsActivity = ContextUtils.activityFromContext(context) != null; 1257 if (contextWrapsActivity) { 1258 ActivityWindowAndroid activityWindow; 1259 try (ScopedSysTraceEvent e2 = 1260 ScopedSysTraceEvent.scoped("AwContents.createActivityWindow")) { 1261 final boolean listenToActivityState = false; 1262 activityWindow = new ActivityWindowAndroid(context, listenToActivityState); 1263 } 1264 wrapper = new WindowAndroidWrapper(activityWindow); 1265 } else { 1266 wrapper = new WindowAndroidWrapper(new WindowAndroid(context)); 1267 } 1268 sContextWindowMap.put(context, wrapper); 1269 } 1270 return wrapper; 1271 } 1272 1273 /** 1274 * Set current locales to native. Propagates this information to the Accept-Language header for 1275 * subsequent requests. Note that this will affect <b>all</b> AwContents, not just this 1276 * instance, as all WebViews share the same NetworkContext/UrlRequestContextGetter. 1277 */ 1278 @VisibleForTesting updateDefaultLocale()1279 public void updateDefaultLocale() { 1280 String locales = LocaleUtils.getDefaultLocaleListString(); 1281 if (!sCurrentLocales.equals(locales)) { 1282 sCurrentLocales = locales; 1283 1284 // We cannot use the first language in sCurrentLocales for the UI language even on 1285 // Android N. LocaleUtils.getDefaultLocaleString() is capable for UI language but 1286 // it is not guaranteed to be listed at the first of sCurrentLocales. Therefore, 1287 // both values are passed to native. 1288 AwContentsJni.get().updateDefaultLocale( 1289 LocaleUtils.getDefaultLocaleString(), sCurrentLocales); 1290 mSettings.updateAcceptLanguages(); 1291 } 1292 } 1293 setFunctor(AwFunctor functor)1294 private void setFunctor(AwFunctor functor) { 1295 if (mDrawFunctor == functor) return; 1296 AwFunctor oldFunctor = mDrawFunctor; 1297 mDrawFunctor = functor; 1298 updateNativeAwGLFunctor(); 1299 1300 if (oldFunctor != null) oldFunctor.destroy(); 1301 } 1302 updateNativeAwGLFunctor()1303 private void updateNativeAwGLFunctor() { 1304 AwContentsJni.get().setCompositorFrameConsumer(mNativeAwContents, AwContents.this, 1305 mDrawFunctor != null ? mDrawFunctor.getNativeCompositorFrameConsumer() : 0); 1306 } 1307 1308 /* Common initialization routine for adopting a native AwContents instance into this 1309 * java instance. 1310 * 1311 * TAKE CARE! This method can get called multiple times per java instance. Code accordingly. 1312 * ^^^^^^^^^ See the native class declaration for more details on relative object lifetimes. 1313 */ setNewAwContents(long newAwContentsPtr)1314 private void setNewAwContents(long newAwContentsPtr) { 1315 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { 1316 setNewAwContentsPreO(newAwContentsPtr); 1317 } else { 1318 // Move the TextClassifier to the new WebContents. 1319 TextClassifier textClassifier = mWebContents != null ? getTextClassifier() : null; 1320 setNewAwContentsPreO(newAwContentsPtr); 1321 if (textClassifier != null) setTextClassifier(textClassifier); 1322 } 1323 if (mContentCaptureConsumer != null) { 1324 mContentCaptureConsumer.onWebContentsChanged(mWebContents); 1325 } 1326 } 1327 1328 // Helper for setNewAwContents containing everything which applies to pre-O. setNewAwContentsPreO(long newAwContentsPtr)1329 private void setNewAwContentsPreO(long newAwContentsPtr) { 1330 if (mNativeAwContents != 0) { 1331 destroyNatives(); 1332 mWebContents = null; 1333 mWebContentsInternalsHolder = null; 1334 mWebContentsInternals = null; 1335 mNavigationController = null; 1336 mJavascriptInjector = null; 1337 } 1338 1339 assert mNativeAwContents == 0 && mCleanupReference == null && mWebContents == null; 1340 1341 mNativeAwContents = newAwContentsPtr; 1342 updateNativeAwGLFunctor(); 1343 1344 mWebContents = AwContentsJni.get().getWebContents(mNativeAwContents, AwContents.this); 1345 mBrowserContext = AwContentsJni.get().getBrowserContext(mNativeAwContents, AwContents.this); 1346 1347 mWindowAndroid = getWindowAndroid(mContext); 1348 mViewAndroidDelegate = 1349 new AwViewAndroidDelegate(mContainerView, mContentsClient, mScrollOffsetManager); 1350 mWebContentsInternalsHolder = new WebContentsInternalsHolder(this); 1351 initWebContents(mViewAndroidDelegate, mInternalAccessAdapter, mWebContents, 1352 mWindowAndroid.getWindowAndroid(), mWebContentsInternalsHolder); 1353 AwContentsJni.get().setJavaPeers(mNativeAwContents, AwContents.this, this, 1354 mWebContentsDelegate, mContentsClientBridge, mIoThreadClient, 1355 mInterceptNavigationDelegate, mAutofillProvider); 1356 GestureListenerManager.fromWebContents(mWebContents) 1357 .addListener(new AwGestureStateListener()); 1358 1359 mNavigationController = mWebContents.getNavigationController(); 1360 installWebContentsObserver(); 1361 mSettings.setWebContents(mWebContents); 1362 if (mAutofillProvider != null) mAutofillProvider.setWebContents(mWebContents); 1363 1364 mDisplayObserver.onDIPScaleChanged(getDeviceScaleFactor()); 1365 1366 updateWebContentsVisibility(); 1367 1368 // The native side object has been bound to this java instance, so now is the time to 1369 // bind all the native->java relationships. 1370 mCleanupReference = new CleanupReference( 1371 this, new AwContentsDestroyRunnable(mNativeAwContents, mWindowAndroid)); 1372 } 1373 installWebContentsObserver()1374 private void installWebContentsObserver() { 1375 if (mWebContentsObserver != null) { 1376 mWebContentsObserver.destroy(); 1377 } 1378 mWebContentsObserver = new AwWebContentsObserver(mWebContents, this, mContentsClient); 1379 } 1380 1381 /** 1382 * Called on the "source" AwContents that is opening the popup window to 1383 * provide the AwContents to host the pop up content. 1384 * 1385 * See //android_webview/docs/how-does-on-create-window-work.md for more details. 1386 */ supplyContentsForPopup(AwContents newContents)1387 public void supplyContentsForPopup(AwContents newContents) { 1388 if (isDestroyed(WARN)) return; 1389 long popupNativeAwContents = 1390 AwContentsJni.get().releasePopupAwContents(mNativeAwContents, AwContents.this); 1391 if (popupNativeAwContents == 0) { 1392 Log.w(TAG, "Popup WebView bind failed: no pending content."); 1393 if (newContents != null) newContents.destroy(); 1394 return; 1395 } 1396 if (newContents == null) { 1397 AwContentsJni.get().destroy(popupNativeAwContents); 1398 return; 1399 } 1400 1401 newContents.receivePopupContents(popupNativeAwContents); 1402 } 1403 1404 // Recap: supplyContentsForPopup() is called on the parent window's content, this method is 1405 // called on the popup window's content. 1406 // See //android_webview/docs/how-does-on-create-window-work.md for more details. receivePopupContents(long popupNativeAwContents)1407 private void receivePopupContents(long popupNativeAwContents) { 1408 if (isDestroyed(WARN)) return; 1409 // Save existing view state. 1410 final boolean wasAttached = mIsAttachedToWindow; 1411 final boolean wasViewVisible = mIsViewVisible; 1412 final boolean wasWindowVisible = mIsWindowVisible; 1413 final boolean wasPaused = mIsPaused; 1414 final boolean wasFocused = mContainerViewFocused; 1415 final boolean wasWindowFocused = mWindowFocused; 1416 1417 // Properly clean up existing mNativeAwContents. 1418 if (wasFocused) onFocusChanged(false, 0, null); 1419 if (wasWindowFocused) onWindowFocusChanged(false); 1420 if (wasViewVisible) setViewVisibilityInternal(false); 1421 if (wasWindowVisible) setWindowVisibilityInternal(false); 1422 if (wasAttached) onDetachedFromWindow(); 1423 if (!wasPaused) onPause(); 1424 1425 // Save injected JavaScript interfaces. 1426 Map<String, Pair<Object, Class>> javascriptInterfaces = 1427 new HashMap<String, Pair<Object, Class>>(); 1428 if (mWebContents != null) { 1429 javascriptInterfaces.putAll(getJavascriptInjector().getInterfaces()); 1430 } 1431 1432 // Save injected WebMessageListeners 1433 WebMessageListenerInfo[] listeners = AwContentsJni.get().getJsObjectsInfo( 1434 mNativeAwContents, AwContents.this, WebMessageListenerInfo.class); 1435 1436 setNewAwContents(popupNativeAwContents); 1437 // We defer loading any URL on the popup until it has been properly initialized (through 1438 // setNewAwContents). We resume the load here. 1439 AwContentsJni.get().resumeLoadingCreatedPopupWebContents( 1440 mNativeAwContents, AwContents.this); 1441 1442 // Finally refresh all view state for mNativeAwContents. 1443 if (!wasPaused) onResume(); 1444 if (wasAttached) { 1445 onAttachedToWindow(); 1446 postInvalidateOnAnimation(); 1447 } 1448 onSizeChanged(mContainerView.getWidth(), mContainerView.getHeight(), 0, 0); 1449 if (wasWindowVisible) setWindowVisibilityInternal(true); 1450 if (wasViewVisible) setViewVisibilityInternal(true); 1451 if (wasWindowFocused) onWindowFocusChanged(wasWindowFocused); 1452 if (wasFocused) onFocusChanged(true, 0, null); 1453 1454 mIsPopupWindow = true; 1455 1456 // Restore injected JavaScript interfaces. 1457 for (Map.Entry<String, Pair<Object, Class>> entry : javascriptInterfaces.entrySet()) { 1458 @SuppressWarnings("unchecked") 1459 Class<? extends Annotation> requiredAnnotation = entry.getValue().second; 1460 getJavascriptInjector().addPossiblyUnsafeInterface( 1461 entry.getValue().first, entry.getKey(), requiredAnnotation); 1462 } 1463 1464 // Restore injected WebMessageListeners. 1465 if (listeners != null) { 1466 for (WebMessageListenerInfo info : listeners) { 1467 addWebMessageListener( 1468 info.mObjectName, info.mAllowedOriginRules, info.mHolder.getListener()); 1469 } 1470 } 1471 } 1472 getJavascriptInjector()1473 private JavascriptInjector getJavascriptInjector() { 1474 if (mJavascriptInjector == null) { 1475 mJavascriptInjector = JavascriptInjector.fromWebContents(mWebContents); 1476 } 1477 return mJavascriptInjector; 1478 } 1479 1480 @CalledByNative onRendererResponsive(AwRenderProcess renderProcess)1481 private void onRendererResponsive(AwRenderProcess renderProcess) { 1482 if (isDestroyed(NO_WARN)) return; 1483 AwThreadUtils.postToCurrentLooper( 1484 () -> mContentsClient.onRendererResponsive(renderProcess)); 1485 } 1486 1487 @CalledByNative onRendererUnresponsive(AwRenderProcess renderProcess)1488 private void onRendererUnresponsive(AwRenderProcess renderProcess) { 1489 if (isDestroyed(NO_WARN)) return; 1490 AwThreadUtils.postToCurrentLooper( 1491 () -> mContentsClient.onRendererUnresponsive(renderProcess)); 1492 } 1493 1494 @VisibleForTesting 1495 @CalledByNativeUnchecked onRenderProcessGone(int childProcessID, boolean crashed)1496 protected boolean onRenderProcessGone(int childProcessID, boolean crashed) { 1497 if (isDestroyed(NO_WARN)) return true; 1498 return mContentsClient.onRenderProcessGone(new AwRenderProcessGoneDetail(crashed, 1499 AwContentsJni.get().getEffectivePriority(mNativeAwContents, AwContents.this))); 1500 } 1501 1502 @VisibleForTesting getEffectivePriorityForTesting()1503 public @RendererPriority int getEffectivePriorityForTesting() { 1504 assert !isDestroyed(NO_WARN); 1505 return AwContentsJni.get().getEffectivePriority(mNativeAwContents, AwContents.this); 1506 } 1507 1508 /** 1509 * Destroys this object and deletes its native counterpart. 1510 */ destroy()1511 public void destroy() { 1512 if (TRACE) Log.i(TAG, "%s destroy", this); 1513 if (isDestroyed(NO_WARN)) return; 1514 1515 if (mContentCaptureConsumer != null) { 1516 mContentCaptureConsumer.onWebContentsChanged(null); 1517 mContentCaptureConsumer = null; 1518 } 1519 1520 // Remove pending messages 1521 mContentsClient.getCallbackHelper().removeCallbacksAndMessages(); 1522 1523 if (mIsAttachedToWindow) { 1524 Log.w(TAG, "WebView.destroy() called while WebView is still attached to window."); 1525 // Need to call detach to avoid leaks because the real detach later will be ignored. 1526 onDetachedFromWindow(); 1527 } 1528 mIsDestroyed = true; 1529 PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> destroyNatives()); 1530 } 1531 1532 /** 1533 * Deletes the native counterpart of this object. 1534 */ 1535 @VisibleForTesting destroyNatives()1536 public void destroyNatives() { 1537 if (mCleanupReference != null) { 1538 assert mNativeAwContents != 0; 1539 1540 mWebContentsObserver.destroy(); 1541 mWebContentsObserver = null; 1542 mNativeAwContents = 0; 1543 mWebContents = null; 1544 mWebContentsInternals = null; 1545 mNavigationController = null; 1546 1547 mCleanupReference.cleanupNow(); 1548 mCleanupReference = null; 1549 } 1550 1551 assert mWebContents == null; 1552 assert mNavigationController == null; 1553 assert mNativeAwContents == 0; 1554 1555 onDestroyed(); 1556 } 1557 1558 @VisibleForTesting onDestroyed()1559 protected void onDestroyed() {} 1560 1561 /** 1562 * Returns whether this instance of WebView is flagged as destroyed. 1563 * If {@link WARN} is passed as a parameter, the method also issues a warning 1564 * log message and dumps stack, as embedders are advised not to call any 1565 * methods on destroyed WebViews. 1566 * 1567 * @param warnIfDestroyed use {@link WARN} if the check is done from a method 1568 * that is called via public WebView API, and {@link NO_WARN} otherwise. 1569 * @return whether this instance of WebView is flagged as destroyed. 1570 */ isDestroyed(int warnIfDestroyed)1571 private boolean isDestroyed(int warnIfDestroyed) { 1572 if (mIsDestroyed && warnIfDestroyed == WARN) { 1573 Log.w(TAG, "Application attempted to call on a destroyed WebView", new Throwable()); 1574 } 1575 boolean destroyRunnableHasRun = 1576 mCleanupReference != null && mCleanupReference.hasCleanedUp(); 1577 boolean weakRefsCleared = 1578 mWebContentsInternalsHolder != null && mWebContentsInternalsHolder.weakRefCleared(); 1579 if (TRACE && destroyRunnableHasRun && !mIsDestroyed) { 1580 // Swallow the error. App developers are not going to do anything with an error msg. 1581 Log.d(TAG, "AwContents is kept alive past CleanupReference by finalizer"); 1582 } 1583 return mIsDestroyed || destroyRunnableHasRun || weakRefsCleared; 1584 } 1585 1586 @VisibleForTesting getWebContents()1587 public WebContents getWebContents() { 1588 return mWebContents; 1589 } 1590 1591 @VisibleForTesting getNavigationController()1592 public NavigationController getNavigationController() { 1593 return mNavigationController; 1594 } 1595 1596 // Can be called from any thread. getSettings()1597 public AwSettings getSettings() { 1598 return mSettings; 1599 } 1600 getContainerView()1601 ViewGroup getContainerView() { 1602 return mContainerView; 1603 } 1604 getPdfExporter()1605 public AwPdfExporter getPdfExporter() { 1606 if (isDestroyed(WARN)) return null; 1607 if (mAwPdfExporter == null) { 1608 mAwPdfExporter = new AwPdfExporter(mContainerView); 1609 AwContentsJni.get().createPdfExporter( 1610 mNativeAwContents, AwContents.this, mAwPdfExporter); 1611 } 1612 return mAwPdfExporter; 1613 } 1614 setAwDrawSWFunctionTable(long functionTablePointer)1615 public static void setAwDrawSWFunctionTable(long functionTablePointer) { 1616 AwContentsJni.get().setAwDrawSWFunctionTable(functionTablePointer); 1617 } 1618 setAwDrawGLFunctionTable(long functionTablePointer)1619 public static void setAwDrawGLFunctionTable(long functionTablePointer) { 1620 AwContentsJni.get().setAwDrawGLFunctionTable(functionTablePointer); 1621 } 1622 getAwDrawGLFunction()1623 public static long getAwDrawGLFunction() { 1624 return AwGLFunctor.getAwDrawGLFunction(); 1625 } 1626 setShouldDownloadFavicons()1627 public static void setShouldDownloadFavicons() { 1628 AwContentsJni.get().setShouldDownloadFavicons(); 1629 } 1630 1631 /** 1632 * Disables contents of JS-to-Java bridge objects to be inspectable using 1633 * Object.keys() method and "for .. in" loops. This is intended for applications 1634 * targeting earlier Android releases where this was not possible, and we want 1635 * to ensure backwards compatible behavior. 1636 */ disableJavascriptInterfacesInspection()1637 public void disableJavascriptInterfacesInspection() { 1638 if (!isDestroyed(WARN)) { 1639 getJavascriptInjector().setAllowInspection(false); 1640 } 1641 } 1642 1643 /** 1644 * Intended for test code. 1645 * @return the number of native instances of this class. 1646 */ 1647 @VisibleForTesting getNativeInstanceCount()1648 public static int getNativeInstanceCount() { 1649 return AwContentsJni.get().getNativeInstanceCount(); 1650 } 1651 1652 // This is only to avoid heap allocations inside getGlobalVisibleRect. It should treated 1653 // as a local variable in the function and not used anywhere else. 1654 private static final Rect sLocalGlobalVisibleRect = new Rect(); 1655 getGlobalVisibleRect()1656 private Rect getGlobalVisibleRect() { 1657 if (!mContainerView.getGlobalVisibleRect(sLocalGlobalVisibleRect)) { 1658 sLocalGlobalVisibleRect.setEmpty(); 1659 } 1660 return sLocalGlobalVisibleRect; 1661 } 1662 setContentCaptureConsumer(ContentCaptureConsumer consumer)1663 public void setContentCaptureConsumer(ContentCaptureConsumer consumer) { 1664 mContentCaptureConsumer = consumer; 1665 } 1666 1667 //-------------------------------------------------------------------------------------------- 1668 // WebView[Provider] method implementations 1669 //-------------------------------------------------------------------------------------------- 1670 onDraw(Canvas canvas)1671 public void onDraw(Canvas canvas) { 1672 try { 1673 TraceEvent.begin("AwContents.onDraw"); 1674 mAwViewMethods.onDraw(canvas); 1675 } finally { 1676 TraceEvent.end("AwContents.onDraw"); 1677 } 1678 } 1679 setLayoutParams(final ViewGroup.LayoutParams layoutParams)1680 public void setLayoutParams(final ViewGroup.LayoutParams layoutParams) { 1681 mLayoutSizer.onLayoutParamsChange(); 1682 } 1683 onMeasure(int widthMeasureSpec, int heightMeasureSpec)1684 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1685 mAwViewMethods.onMeasure(widthMeasureSpec, heightMeasureSpec); 1686 } 1687 getContentHeightCss()1688 public int getContentHeightCss() { 1689 if (isDestroyed(WARN)) return 0; 1690 return (int) Math.ceil(mContentHeightDip); 1691 } 1692 getContentWidthCss()1693 public int getContentWidthCss() { 1694 if (isDestroyed(WARN)) return 0; 1695 return (int) Math.ceil(mContentWidthDip); 1696 } 1697 capturePicture()1698 public Picture capturePicture() { 1699 if (TRACE) Log.i(TAG, "%s capturePicture", this); 1700 if (isDestroyed(WARN)) return null; 1701 return new AwPicture(AwContentsJni.get().capturePicture(mNativeAwContents, AwContents.this, 1702 mScrollOffsetManager.computeHorizontalScrollRange(), 1703 mScrollOffsetManager.computeVerticalScrollRange())); 1704 } 1705 clearView()1706 public void clearView() { 1707 if (TRACE) Log.i(TAG, "%s clearView", this); 1708 if (!isDestroyed(WARN)) AwContentsJni.get().clearView(mNativeAwContents, AwContents.this); 1709 } 1710 1711 /** 1712 * Enable the onNewPicture callback. 1713 * @param enabled Flag to enable the callback. 1714 * @param invalidationOnly Flag to call back only on invalidation without providing a picture. 1715 */ enableOnNewPicture(boolean enabled, boolean invalidationOnly)1716 public void enableOnNewPicture(boolean enabled, boolean invalidationOnly) { 1717 if (TRACE) Log.i(TAG, "%s enableOnNewPicture=%s", this, enabled); 1718 if (isDestroyed(WARN)) return; 1719 if (invalidationOnly) { 1720 mPictureListenerContentProvider = null; 1721 } else if (enabled && mPictureListenerContentProvider == null) { 1722 mPictureListenerContentProvider = () -> capturePicture(); 1723 } 1724 AwContentsJni.get().enableOnNewPicture(mNativeAwContents, AwContents.this, enabled); 1725 } 1726 findAllAsync(String searchString)1727 public void findAllAsync(String searchString) { 1728 if (TRACE) Log.i(TAG, "%s findAllAsync", this); 1729 if (isDestroyed(WARN)) return; 1730 if (searchString == null) { 1731 throw new IllegalArgumentException("Search string shouldn't be null"); 1732 } 1733 AwContentsJni.get().findAllAsync(mNativeAwContents, AwContents.this, searchString); 1734 } 1735 findNext(boolean forward)1736 public void findNext(boolean forward) { 1737 if (TRACE) Log.i(TAG, "%s findNext", this); 1738 if (!isDestroyed(WARN)) { 1739 AwContentsJni.get().findNext(mNativeAwContents, AwContents.this, forward); 1740 } 1741 } 1742 clearMatches()1743 public void clearMatches() { 1744 if (TRACE) Log.i(TAG, "%s clearMatches", this); 1745 if (!isDestroyed(WARN)) { 1746 AwContentsJni.get().clearMatches(mNativeAwContents, AwContents.this); 1747 } 1748 } 1749 1750 /** 1751 * @return load progress of the WebContents, on a scale of 0-100. 1752 */ getMostRecentProgress()1753 public int getMostRecentProgress() { 1754 if (isDestroyed(WARN)) return 0; 1755 if (!mWebContents.isLoading()) return 100; 1756 return Math.round(100 * mWebContents.getLoadProgress()); 1757 } 1758 getFavicon()1759 public Bitmap getFavicon() { 1760 if (isDestroyed(WARN)) return null; 1761 return mFavicon; 1762 } 1763 requestVisitedHistoryFromClient()1764 private void requestVisitedHistoryFromClient() { 1765 Callback<String[]> callback = value -> { 1766 if (value != null) { 1767 // Replace null values with empty strings, because they can't be represented as 1768 // native strings. 1769 for (int i = 0; i < value.length; i++) { 1770 if (value[i] == null) value[i] = ""; 1771 } 1772 } 1773 1774 PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> { 1775 if (!isDestroyed(NO_WARN)) { 1776 AwContentsJni.get().addVisitedLinks(mNativeAwContents, AwContents.this, value); 1777 } 1778 }); 1779 }; 1780 mContentsClient.getVisitedHistory(callback); 1781 } 1782 1783 private static final Pattern BAD_HEADER_CHAR = Pattern.compile("[\u0000\r\n]"); 1784 1785 /** 1786 * WebView.loadUrl. 1787 */ loadUrl(String url, Map<String, String> additionalHttpHeaders)1788 public void loadUrl(String url, Map<String, String> additionalHttpHeaders) { 1789 if (TRACE) Log.i(TAG, "%s loadUrl(extra headers)=%s", this, url); 1790 if (isDestroyed(WARN)) return; 1791 // Early out to match old WebView implementation 1792 if (url == null) { 1793 return; 1794 } 1795 // TODO: We may actually want to do some sanity checks here (like filter about://chrome). 1796 1797 // For backwards compatibility, apps targeting less than K will have JS URLs evaluated 1798 // directly and any result of the evaluation will not replace the current page content. 1799 // Matching Chrome behavior more closely; apps targetting >= K that load a JS URL will 1800 // have the result of that URL replace the content of the current page. 1801 final String javaScriptScheme = "javascript:"; 1802 if (mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT && url.startsWith(javaScriptScheme)) { 1803 evaluateJavaScript(url.substring(javaScriptScheme.length()), null); 1804 return; 1805 } 1806 1807 LoadUrlParams params = new LoadUrlParams(url, PageTransition.TYPED); 1808 if (additionalHttpHeaders != null) { 1809 boolean valid = true; 1810 for (Map.Entry<String, String> header : additionalHttpHeaders.entrySet()) { 1811 String headerName = header.getKey(); 1812 String headerValue = header.getValue(); 1813 if ((headerName != null && BAD_HEADER_CHAR.matcher(headerName).find()) 1814 || (headerValue != null && BAD_HEADER_CHAR.matcher(headerValue).find())) { 1815 valid = false; 1816 break; 1817 } 1818 } 1819 RecordHistogram.recordBooleanHistogram("Android.WebView.ExtraHeaders.Valid", valid); 1820 params.setExtraHeaders(new HashMap<String, String>(additionalHttpHeaders)); 1821 } 1822 1823 loadUrl(params); 1824 } 1825 1826 /** 1827 * WebView.loadUrl. 1828 */ loadUrl(String url)1829 public void loadUrl(String url) { 1830 if (TRACE) Log.i(TAG, "%s loadUrl=%s", this, url); 1831 if (isDestroyed(WARN)) return; 1832 // Early out to match old WebView implementation 1833 if (url == null) { 1834 return; 1835 } 1836 loadUrl(url, null); 1837 } 1838 1839 /** 1840 * WebView.postUrl. 1841 */ postUrl(String url, byte[] postData)1842 public void postUrl(String url, byte[] postData) { 1843 if (TRACE) Log.i(TAG, "%s postUrl=%s", this, url); 1844 if (isDestroyed(WARN)) return; 1845 LoadUrlParams params = LoadUrlParams.createLoadHttpPostParams(url, postData); 1846 Map<String, String> headers = new HashMap<String, String>(); 1847 headers.put("Content-Type", "application/x-www-form-urlencoded"); 1848 params.setExtraHeaders(headers); 1849 loadUrl(params); 1850 } 1851 fixupMimeType(String mimeType)1852 private static String fixupMimeType(String mimeType) { 1853 return TextUtils.isEmpty(mimeType) ? "text/html" : mimeType; 1854 } 1855 fixupData(String data)1856 private static String fixupData(String data) { 1857 return TextUtils.isEmpty(data) ? "" : data; 1858 } 1859 fixupBase(String url)1860 private static String fixupBase(String url) { 1861 return TextUtils.isEmpty(url) ? ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL : url; 1862 } 1863 fixupHistory(String url)1864 private static String fixupHistory(String url) { 1865 return TextUtils.isEmpty(url) ? ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL : url; 1866 } 1867 isBase64Encoded(String encoding)1868 private static boolean isBase64Encoded(String encoding) { 1869 return "base64".equals(encoding); 1870 } 1871 recordHistoryUrl(@istoryUrl int value)1872 private static void recordHistoryUrl(@HistoryUrl int value) { 1873 RecordHistogram.recordEnumeratedHistogram( 1874 "Android.WebView.LoadDataWithBaseUrl.HistoryUrl", value, HistoryUrl.COUNT); 1875 } 1876 recordBaseUrl(@rlScheme int value)1877 private static void recordBaseUrl(@UrlScheme int value) { 1878 RecordHistogram.recordEnumeratedHistogram( 1879 DATA_BASE_URL_SCHEME_HISTOGRAM_NAME, value, UrlScheme.COUNT); 1880 } 1881 recordLoadUrlScheme(@rlScheme int value)1882 private static void recordLoadUrlScheme(@UrlScheme int value) { 1883 RecordHistogram.recordEnumeratedHistogram( 1884 LOAD_URL_SCHEME_HISTOGRAM_NAME, value, UrlScheme.COUNT); 1885 } 1886 1887 /** 1888 * WebView.loadData. 1889 */ loadData(String data, String mimeType, String encoding)1890 public void loadData(String data, String mimeType, String encoding) { 1891 if (TRACE) Log.i(TAG, "%s loadData", this); 1892 if (isDestroyed(WARN)) return; 1893 if (data != null && data.contains("#")) { 1894 if (!BuildInfo.targetsAtLeastQ() && !isBase64Encoded(encoding)) { 1895 // As of Chromium M72, data URI parsing strictly enforces encoding of '#'. To 1896 // support WebView applications which were not expecting this change, we do it for 1897 // them. 1898 data = fixupOctothorpesInLoadDataContent(data); 1899 } 1900 } 1901 loadUrl(LoadUrlParams.createLoadDataParams( 1902 fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding))); 1903 } 1904 1905 /** 1906 * Helper method to fixup content passed to {@link #loadData} which may not have had '#' 1907 * characters encoded correctly. Historically Chromium did not strictly enforce the encoding of 1908 * '#' characters in Data URLs; they would be treated both as renderable content and as 1909 * potential URL fragments for DOM id matching. This behavior changed in Chromium M72 where 1910 * stricter parsing was enforced; the first '#' character now marks the end of the renderable 1911 * section and the start of the DOM fragment. 1912 * 1913 * @param data The content passed to {@link #loadData}, which may contain unencoded '#'s. 1914 * @return A version of the input with '#' characters correctly encoded, preserving any DOM id 1915 * selector which may have been present in the original. 1916 */ 1917 @VisibleForTesting fixupOctothorpesInLoadDataContent(String data)1918 public static String fixupOctothorpesInLoadDataContent(String data) { 1919 // If the data may have had a valid DOM selector, we duplicate the selector and append it as 1920 // a proper URL fragment. For example, "<a id='target'>Target</a>#target" will be converted 1921 // to "<a id='target'>Target</a>%23target#target". This preserves both the rendering (which 1922 // should render 'Target#target' on the page) and the DOM selector behavior (which should 1923 // scroll to the anchor). 1924 Matcher matcher = sDataURLWithSelectorPattern.matcher(data); 1925 String suffix = matcher.matches() ? matcher.group(1) : ""; 1926 return data.replace("#", "%23") + suffix; 1927 } 1928 schemeForUrl(String url)1929 private @UrlScheme int schemeForUrl(String url) { 1930 if (url == null || url.equals(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL)) { 1931 return (UrlScheme.EMPTY); 1932 } else if (url.startsWith("http:")) { 1933 return (UrlScheme.HTTP_SCHEME); 1934 } else if (url.startsWith("https:")) { 1935 return (UrlScheme.HTTPS_SCHEME); 1936 } else if (sFileAndroidAssetPattern.matcher(url).matches()) { 1937 return (UrlScheme.FILE_ANDROID_ASSET_SCHEME); 1938 } else if (url.startsWith("file:")) { 1939 return (UrlScheme.FILE_SCHEME); 1940 } else if (url.startsWith("ftp:")) { 1941 return (UrlScheme.FTP_SCHEME); 1942 } else if (url.startsWith("data:")) { 1943 return (UrlScheme.DATA_SCHEME); 1944 } else if (url.startsWith("javascript:")) { 1945 return (UrlScheme.JAVASCRIPT_SCHEME); 1946 } else if (url.startsWith("about:")) { 1947 return (UrlScheme.ABOUT_SCHEME); 1948 } else if (url.startsWith("chrome:")) { 1949 return (UrlScheme.CHROME_SCHEME); 1950 } else if (url.startsWith("blob:")) { 1951 return (UrlScheme.BLOB_SCHEME); 1952 } else if (url.startsWith("content:")) { 1953 return (UrlScheme.CONTENT_SCHEME); 1954 } else if (url.startsWith("intent:")) { 1955 return (UrlScheme.INTENT_SCHEME); 1956 } 1957 return (UrlScheme.UNKNOWN_SCHEME); 1958 } 1959 1960 /** 1961 * WebView.loadDataWithBaseURL. 1962 */ loadDataWithBaseURL( String baseUrl, String data, String mimeType, String encoding, String historyUrl)1963 public void loadDataWithBaseURL( 1964 String baseUrl, String data, String mimeType, String encoding, String historyUrl) { 1965 if (TRACE) Log.i(TAG, "%s loadDataWithBaseURL=%s", this, baseUrl); 1966 if (isDestroyed(WARN)) return; 1967 1968 data = fixupData(data); 1969 mimeType = fixupMimeType(mimeType); 1970 LoadUrlParams loadUrlParams; 1971 baseUrl = fixupBase(baseUrl); 1972 historyUrl = fixupHistory(historyUrl); 1973 1974 if (historyUrl.equals(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL)) { 1975 recordHistoryUrl(HistoryUrl.EMPTY); 1976 } else if (historyUrl.equals(baseUrl)) { 1977 recordHistoryUrl(HistoryUrl.BASEURL); 1978 } else { 1979 recordHistoryUrl(HistoryUrl.DIFFERENT); 1980 } 1981 1982 recordBaseUrl(schemeForUrl(baseUrl)); 1983 1984 if (baseUrl.startsWith("data:")) { 1985 // For backwards compatibility with WebViewClassic, we use the value of |encoding| 1986 // as the charset, as long as it's not "base64". 1987 boolean isBase64 = isBase64Encoded(encoding); 1988 loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl( 1989 data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding); 1990 } else { 1991 // When loading data with a non-data: base URL, the classic WebView would effectively 1992 // "dump" that string of data into the WebView without going through regular URL 1993 // loading steps such as decoding URL-encoded entities. We achieve this same behavior by 1994 // base64 encoding the data that is passed here and then loading that as a data: URL. 1995 try { 1996 loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl( 1997 Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType, 1998 true, baseUrl, historyUrl, "utf-8"); 1999 } catch (java.io.UnsupportedEncodingException e) { 2000 Log.wtf(TAG, "Unable to load data string %s", data, e); 2001 return; 2002 } 2003 } 2004 2005 // This is a workaround for an issue with PlzNavigate and one of Samsung's OEM mail apps. 2006 // See http://crbug.com/781535. 2007 if (isSamsungMailApp() && SAMSUNG_WORKAROUND_BASE_URL.equals(loadUrlParams.getBaseUrl())) { 2008 PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, 2009 () -> loadUrl(loadUrlParams), SAMSUNG_WORKAROUND_DELAY); 2010 return; 2011 } 2012 loadUrl(loadUrlParams); 2013 } 2014 2015 /** 2016 * Load url without fixing up the url string. Consumers of ContentView are responsible for 2017 * ensuring the URL passed in is properly formatted (i.e. the scheme has been added if left 2018 * off during user input). 2019 * 2020 * @param params Parameters for this load. 2021 */ 2022 @VisibleForTesting loadUrl(LoadUrlParams params)2023 public void loadUrl(LoadUrlParams params) { 2024 if (params.getBaseUrl() == null) { 2025 // Don't record the URL if this was loaded via loadDataWithBaseURL(). That API is 2026 // tracked separately under Android.WebView.LoadDataWithBaseUrl.BaseUrl. 2027 recordLoadUrlScheme(schemeForUrl(params.getUrl())); 2028 } 2029 2030 if (params.getLoadUrlType() == LoadURLType.DATA && !params.isBaseUrlDataScheme()) { 2031 // This allows data URLs with a non-data base URL access to file:///android_asset/ and 2032 // file:///android_res/ URLs. If AwSettings.getAllowFileAccess permits, it will also 2033 // allow access to file:// URLs (subject to OS level permission checks). 2034 params.setCanLoadLocalResources(true); 2035 AwContentsJni.get().grantFileSchemeAccesstoChildProcess( 2036 mNativeAwContents, AwContents.this); 2037 } 2038 2039 // If we are reloading the same url, then set transition type as reload. 2040 if (params.getUrl() != null && params.getUrl().equals(mWebContents.getLastCommittedUrl()) 2041 && params.getTransitionType() == PageTransition.TYPED) { 2042 params.setTransitionType(PageTransition.RELOAD); 2043 } 2044 params.setTransitionType( 2045 params.getTransitionType() | PageTransition.FROM_API); 2046 2047 // For WebView, always use the user agent override, which is set 2048 // every time the user agent in AwSettings is modified. 2049 params.setOverrideUserAgent(UserAgentOverrideOption.TRUE); 2050 2051 2052 // We don't pass extra headers to the content layer, as WebViewClassic 2053 // was adding them in a very narrow set of conditions. See http://crbug.com/306873 2054 // However, if the embedder is attempting to inject a Referer header for their 2055 // loadUrl call, then we set that separately and remove it from the extra headers map/ 2056 final String referer = "referer"; 2057 Map<String, String> extraHeaders = params.getExtraHeaders(); 2058 if (extraHeaders != null) { 2059 for (String header : extraHeaders.keySet()) { 2060 if (referer.equals(header.toLowerCase(Locale.US))) { 2061 params.setReferrer( 2062 new Referrer(extraHeaders.remove(header), ReferrerPolicy.DEFAULT)); 2063 params.setExtraHeaders(extraHeaders); 2064 break; 2065 } 2066 } 2067 } 2068 2069 AwContentsJni.get().setExtraHeadersForUrl(mNativeAwContents, AwContents.this, 2070 params.getUrl(), params.getExtraHttpRequestHeadersString()); 2071 params.setExtraHeaders(new HashMap<String, String>()); 2072 2073 // Ideally, the URL would only be "fixed" for user input (e.g. for URLs 2074 // entered into the Omnibox), but some WebView API consumers rely on 2075 // the legacy behavior where all navigations were subject to the 2076 // "fixing". See also https://crbug.com/1145717. 2077 params.setUrl(UrlFormatter.fixupUrl(params.getUrl()).getPossiblyInvalidSpec()); 2078 2079 mNavigationController.loadUrl(params); 2080 2081 // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit. 2082 // Chromium does not use this use code path and the best emulation of this behavior to call 2083 // request visited links once on the first URL load of the WebView. 2084 if (!mHasRequestedVisitedHistoryFromClient) { 2085 mHasRequestedVisitedHistoryFromClient = true; 2086 requestVisitedHistoryFromClient(); 2087 } 2088 } 2089 2090 /** 2091 * Get the URL of the current page. This is the visible URL of the {@link WebContents} which may 2092 * be a pending navigation or the last committed URL. For the last committed URL use 2093 * #getLastCommittedUrl(). 2094 * 2095 * @return The URL of the current page or null if it's empty. 2096 */ getUrl()2097 public GURL getUrl() { 2098 if (isDestroyed(WARN)) return null; 2099 GURL url = mWebContents.getVisibleUrl(); 2100 if (url == null || url.getSpec().trim().isEmpty()) return null; 2101 return url; 2102 } 2103 2104 /** 2105 * Gets the last committed URL. It represents the current page that is 2106 * displayed in WebContents. It represents the current security context. 2107 * 2108 * @return The URL of the current page or null if it's empty. 2109 */ getLastCommittedUrl()2110 public String getLastCommittedUrl() { 2111 if (isDestroyed(NO_WARN)) return null; 2112 String url = mWebContents.getLastCommittedUrl(); 2113 if (url == null || url.trim().isEmpty()) return null; 2114 return url; 2115 } 2116 requestFocus()2117 public void requestFocus() { 2118 mAwViewMethods.requestFocus(); 2119 } 2120 setBackgroundColor(int color)2121 public void setBackgroundColor(int color) { 2122 mBaseBackgroundColor = color; 2123 mDidInitBackground = true; 2124 if (!isDestroyed(WARN)) { 2125 AwContentsJni.get().setBackgroundColor(mNativeAwContents, AwContents.this, color); 2126 } 2127 } 2128 2129 /** 2130 * @see android.view.View#setLayerType() 2131 */ setLayerType(int layerType, Paint paint)2132 public void setLayerType(int layerType, Paint paint) { 2133 mAwViewMethods.setLayerType(layerType, paint); 2134 } 2135 2136 @VisibleForTesting getEffectiveBackgroundColorForTesting()2137 public int getEffectiveBackgroundColorForTesting() { 2138 return getEffectiveBackgroundColor(); 2139 } 2140 getEffectiveBackgroundColor()2141 int getEffectiveBackgroundColor() { 2142 // Do not ask the WebContents for the background color, as it will always 2143 // report white prior to initial navigation or post destruction, whereas we want 2144 // to use the client supplied base value in those cases. 2145 if (isDestroyed(NO_WARN)) { 2146 return mBaseBackgroundColor; 2147 } else if (!mContentsClient.isCachedRendererBackgroundColorValid()) { 2148 // In force dark mode, if background color not set, this cause a white flash, 2149 // just show black background. 2150 if (mSettings.isDarkMode() && !mDidInitBackground) { 2151 return Color.BLACK; 2152 } 2153 return mBaseBackgroundColor; 2154 } 2155 return mContentsClient.getCachedRendererBackgroundColor(); 2156 } 2157 isMultiTouchZoomSupported()2158 public boolean isMultiTouchZoomSupported() { 2159 return mSettings.supportsMultiTouchZoom(); 2160 } 2161 2162 @VisibleForTesting getZoomControlsViewForTest()2163 public View getZoomControlsViewForTest() { 2164 return mZoomControls.getZoomControlsViewForTest(); 2165 } 2166 2167 @VisibleForTesting getZoomControlsForTest()2168 public AwZoomControls getZoomControlsForTest() { 2169 return mZoomControls; 2170 } 2171 2172 /** 2173 * @see View#setOverScrollMode(int) 2174 */ setOverScrollMode(int mode)2175 public void setOverScrollMode(int mode) { 2176 if (mode != View.OVER_SCROLL_NEVER) { 2177 mOverScrollGlow = new OverScrollGlow(mContext, mContainerView); 2178 } else { 2179 mOverScrollGlow = null; 2180 } 2181 } 2182 2183 // TODO(mkosiba): In WebViewClassic these appear in some of the scroll extent calculation 2184 // methods but toggling them has no visiual effect on the content (in other words the scrolling 2185 // code behaves as if the scrollbar-related padding is in place but the onDraw code doesn't 2186 // take that into consideration). 2187 // http://crbug.com/269032 2188 private boolean mOverlayHorizontalScrollbar = true; 2189 private boolean mOverlayVerticalScrollbar; 2190 2191 /** 2192 * @see View#setScrollBarStyle(int) 2193 */ setScrollBarStyle(int style)2194 public void setScrollBarStyle(int style) { 2195 if (style == View.SCROLLBARS_INSIDE_OVERLAY 2196 || style == View.SCROLLBARS_OUTSIDE_OVERLAY) { 2197 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true; 2198 } else { 2199 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false; 2200 } 2201 } 2202 2203 /** 2204 * @see View#setHorizontalScrollbarOverlay(boolean) 2205 */ setHorizontalScrollbarOverlay(boolean overlay)2206 public void setHorizontalScrollbarOverlay(boolean overlay) { 2207 if (TRACE) Log.i(TAG, "%s setHorizontalScrollbarOverlay=%s", this, overlay); 2208 mOverlayHorizontalScrollbar = overlay; 2209 } 2210 2211 /** 2212 * @see View#setVerticalScrollbarOverlay(boolean) 2213 */ setVerticalScrollbarOverlay(boolean overlay)2214 public void setVerticalScrollbarOverlay(boolean overlay) { 2215 if (TRACE) Log.i(TAG, "%s setVerticalScrollbarOverlay=%s", this, overlay); 2216 mOverlayVerticalScrollbar = overlay; 2217 } 2218 2219 /** 2220 * @see View#overlayHorizontalScrollbar() 2221 */ overlayHorizontalScrollbar()2222 public boolean overlayHorizontalScrollbar() { 2223 return mOverlayHorizontalScrollbar; 2224 } 2225 2226 /** 2227 * @see View#overlayVerticalScrollbar() 2228 */ overlayVerticalScrollbar()2229 public boolean overlayVerticalScrollbar() { 2230 return mOverlayVerticalScrollbar; 2231 } 2232 2233 /** 2234 * Called by the embedder when the scroll offset of the containing view has changed. 2235 * @see View#onScrollChanged(int,int) 2236 */ onContainerViewScrollChanged(int l, int t, int oldl, int oldt)2237 public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) { 2238 mAwViewMethods.onContainerViewScrollChanged(l, t, oldl, oldt); 2239 } 2240 2241 /** 2242 * Called by the embedder when the containing view is to be scrolled or overscrolled. 2243 * @see View#onOverScrolled(int,int,int,int) 2244 */ onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)2245 public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, 2246 boolean clampedY) { 2247 mAwViewMethods.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); 2248 } 2249 2250 /** 2251 * @see android.webkit.WebView#requestChildRectangleOnScreen(View, Rect, boolean) 2252 */ requestChildRectangleOnScreen(View child, Rect rect, boolean immediate)2253 public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) { 2254 if (isDestroyed(WARN)) return false; 2255 return mScrollOffsetManager.requestChildRectangleOnScreen( 2256 child.getLeft() - child.getScrollX(), child.getTop() - child.getScrollY(), 2257 rect, immediate); 2258 } 2259 2260 /** 2261 * @see View#computeHorizontalScrollRange() 2262 */ computeHorizontalScrollRange()2263 public int computeHorizontalScrollRange() { 2264 return mAwViewMethods.computeHorizontalScrollRange(); 2265 } 2266 2267 /** 2268 * @see View#computeHorizontalScrollOffset() 2269 */ computeHorizontalScrollOffset()2270 public int computeHorizontalScrollOffset() { 2271 return mAwViewMethods.computeHorizontalScrollOffset(); 2272 } 2273 2274 /** 2275 * @see View#computeVerticalScrollRange() 2276 */ computeVerticalScrollRange()2277 public int computeVerticalScrollRange() { 2278 return mAwViewMethods.computeVerticalScrollRange(); 2279 } 2280 2281 /** 2282 * @see View#computeVerticalScrollOffset() 2283 */ computeVerticalScrollOffset()2284 public int computeVerticalScrollOffset() { 2285 return mAwViewMethods.computeVerticalScrollOffset(); 2286 } 2287 2288 /** 2289 * @see View#computeVerticalScrollExtent() 2290 */ computeVerticalScrollExtent()2291 public int computeVerticalScrollExtent() { 2292 return mAwViewMethods.computeVerticalScrollExtent(); 2293 } 2294 2295 /** 2296 * @see View.computeScroll() 2297 */ computeScroll()2298 public void computeScroll() { 2299 mAwViewMethods.computeScroll(); 2300 } 2301 2302 /** 2303 * @see View#onCheckIsTextEditor() 2304 */ onCheckIsTextEditor()2305 public boolean onCheckIsTextEditor() { 2306 return mAwViewMethods.onCheckIsTextEditor(); 2307 } 2308 2309 /** 2310 * @see android.webkit.WebView#stopLoading() 2311 */ stopLoading()2312 public void stopLoading() { 2313 if (TRACE) Log.i(TAG, "%s stopLoading", this); 2314 if (!isDestroyed(WARN)) mWebContents.stop(); 2315 } 2316 2317 /** 2318 * @see android.webkit.WebView#reload() 2319 */ reload()2320 public void reload() { 2321 if (TRACE) Log.i(TAG, "%s reload", this); 2322 if (!isDestroyed(WARN)) mNavigationController.reload(true); 2323 } 2324 2325 /** 2326 * @see android.webkit.WebView#canGoBack() 2327 */ canGoBack()2328 public boolean canGoBack() { 2329 return isDestroyed(WARN) ? false : mNavigationController.canGoBack(); 2330 } 2331 2332 /** 2333 * @see android.webkit.WebView#goBack() 2334 */ goBack()2335 public void goBack() { 2336 if (TRACE) Log.i(TAG, "%s goBack", this); 2337 if (!isDestroyed(WARN)) mNavigationController.goBack(); 2338 } 2339 2340 /** 2341 * @see android.webkit.WebView#canGoForward() 2342 */ canGoForward()2343 public boolean canGoForward() { 2344 return isDestroyed(WARN) ? false : mNavigationController.canGoForward(); 2345 } 2346 2347 /** 2348 * @see android.webkit.WebView#goForward() 2349 */ goForward()2350 public void goForward() { 2351 if (TRACE) Log.i(TAG, "%s goForward", this); 2352 if (!isDestroyed(WARN)) mNavigationController.goForward(); 2353 } 2354 2355 /** 2356 * @see android.webkit.WebView#canGoBackOrForward(int) 2357 */ canGoBackOrForward(int steps)2358 public boolean canGoBackOrForward(int steps) { 2359 return isDestroyed(WARN) ? false : mNavigationController.canGoToOffset(steps); 2360 } 2361 2362 /** 2363 * @see android.webkit.WebView#goBackOrForward(int) 2364 */ goBackOrForward(int steps)2365 public void goBackOrForward(int steps) { 2366 if (TRACE) Log.i(TAG, "%s goBackOrForwad=%d", this, steps); 2367 if (!isDestroyed(WARN)) mNavigationController.goToOffset(steps); 2368 } 2369 2370 /** 2371 * @see android.webkit.WebView#pauseTimers() 2372 */ pauseTimers()2373 public void pauseTimers() { 2374 if (TRACE) Log.i(TAG, "%s pauseTimers", this); 2375 if (!isDestroyed(WARN)) { 2376 ContentViewStatics.setWebKitSharedTimersSuspended(true); 2377 } 2378 } 2379 2380 /** 2381 * @see android.webkit.WebView#resumeTimers() 2382 */ resumeTimers()2383 public void resumeTimers() { 2384 if (TRACE) Log.i(TAG, "%s resumeTimers", this); 2385 if (!isDestroyed(WARN)) { 2386 ContentViewStatics.setWebKitSharedTimersSuspended(false); 2387 } 2388 } 2389 2390 /** 2391 * @see android.webkit.WebView#onPause() 2392 */ onPause()2393 public void onPause() { 2394 if (TRACE) Log.i(TAG, "%s onPause", this); 2395 if (mIsPaused || isDestroyed(NO_WARN)) return; 2396 mIsPaused = true; 2397 AwContentsJni.get().setIsPaused(mNativeAwContents, AwContents.this, mIsPaused); 2398 2399 // Geolocation is paused/resumed via the page visibility mechanism. 2400 updateWebContentsVisibility(); 2401 } 2402 2403 /** 2404 * @see android.webkit.WebView#onResume() 2405 */ onResume()2406 public void onResume() { 2407 if (TRACE) Log.i(TAG, "%s onResume", this); 2408 if (!mIsPaused || isDestroyed(NO_WARN)) return; 2409 mIsPaused = false; 2410 AwContentsJni.get().setIsPaused(mNativeAwContents, AwContents.this, mIsPaused); 2411 updateWebContentsVisibility(); 2412 } 2413 2414 /** 2415 * @see android.webkit.WebView#isPaused() 2416 */ isPaused()2417 public boolean isPaused() { 2418 return isDestroyed(WARN) ? false : mIsPaused; 2419 } 2420 2421 /** 2422 * @see android.webkit.WebView#onCreateInputConnection(EditorInfo) 2423 */ onCreateInputConnection(EditorInfo outAttrs)2424 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 2425 return mAwViewMethods.onCreateInputConnection(outAttrs); 2426 } 2427 2428 /** 2429 * @see android.webkit.WebView#onDragEvent(DragEvent) 2430 */ onDragEvent(DragEvent event)2431 public boolean onDragEvent(DragEvent event) { 2432 return mAwViewMethods.onDragEvent(event); 2433 } 2434 2435 /** 2436 * @see android.webkit.WebView#onKeyUp(int, KeyEvent) 2437 */ onKeyUp(int keyCode, KeyEvent event)2438 public boolean onKeyUp(int keyCode, KeyEvent event) { 2439 return mAwViewMethods.onKeyUp(keyCode, event); 2440 } 2441 2442 /** 2443 * @see android.webkit.WebView#dispatchKeyEvent(KeyEvent) 2444 */ dispatchKeyEvent(KeyEvent event)2445 public boolean dispatchKeyEvent(KeyEvent event) { 2446 return mAwViewMethods.dispatchKeyEvent(event); 2447 } 2448 2449 /** 2450 * Clears the resource cache. Note that the cache is per-application, so this will clear the 2451 * cache for all WebViews used. 2452 * 2453 * @param includeDiskFiles if false, only the RAM cache is cleared 2454 */ clearCache(boolean includeDiskFiles)2455 public void clearCache(boolean includeDiskFiles) { 2456 if (TRACE) Log.i(TAG, "%s clearCache", this); 2457 if (!isDestroyed(WARN)) { 2458 AwContentsJni.get().clearCache(mNativeAwContents, AwContents.this, includeDiskFiles); 2459 } 2460 } 2461 documentHasImages(Message message)2462 public void documentHasImages(Message message) { 2463 if (!isDestroyed(WARN)) { 2464 AwContentsJni.get().documentHasImages(mNativeAwContents, AwContents.this, message); 2465 } 2466 } 2467 saveWebArchive( final String basename, boolean autoname, final Callback<String> callback)2468 public void saveWebArchive( 2469 final String basename, boolean autoname, final Callback<String> callback) { 2470 if (TRACE) Log.i(TAG, "%s saveWebArchive=%s", this, basename); 2471 if (!autoname) { 2472 saveWebArchiveInternal(basename, callback); 2473 return; 2474 } 2475 // If auto-generating the file name, handle the name generation on a background thread 2476 // as it will require I/O access for checking whether previous files existed. 2477 new AsyncTask<String>() { 2478 @Override 2479 protected String doInBackground() { 2480 return generateArchiveAutoNamePath(getOriginalUrl(), basename); 2481 } 2482 2483 @Override 2484 protected void onPostExecute(String result) { 2485 saveWebArchiveInternal(result, callback); 2486 } 2487 } 2488 .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 2489 } 2490 getOriginalUrl()2491 public String getOriginalUrl() { 2492 if (isDestroyed(WARN)) return null; 2493 NavigationHistory history = mNavigationController.getNavigationHistory(); 2494 int currentIndex = history.getCurrentEntryIndex(); 2495 if (currentIndex >= 0 && currentIndex < history.getEntryCount()) { 2496 return history.getEntryAtIndex(currentIndex).getOriginalUrl(); 2497 } 2498 return null; 2499 } 2500 2501 /** 2502 * @see NavigationController#getNavigationHistory() 2503 */ getNavigationHistory()2504 public NavigationHistory getNavigationHistory() { 2505 return isDestroyed(WARN) ? null : mNavigationController.getNavigationHistory(); 2506 } 2507 2508 /** 2509 * @see android.webkit.WebView#getTitle() 2510 */ getTitle()2511 public String getTitle() { 2512 return isDestroyed(WARN) ? null : mWebContents.getTitle(); 2513 } 2514 2515 /** 2516 * @see android.webkit.WebView#clearHistory() 2517 */ clearHistory()2518 public void clearHistory() { 2519 if (TRACE) Log.i(TAG, "%s clearHistory", this); 2520 if (!isDestroyed(WARN)) mNavigationController.clearHistory(); 2521 } 2522 2523 /** 2524 * @see android.webkit.WebView#getCertificate() 2525 */ getCertificate()2526 public SslCertificate getCertificate() { 2527 return isDestroyed(WARN) 2528 ? null 2529 : SslUtil.getCertificateFromDerBytes( 2530 AwContentsJni.get().getCertificate(mNativeAwContents, AwContents.this)); 2531 } 2532 2533 /** 2534 * @see android.webkit.WebView#clearSslPreferences() 2535 */ clearSslPreferences()2536 public void clearSslPreferences() { 2537 if (TRACE) Log.i(TAG, "%s clearSslPreferences", this); 2538 if (!isDestroyed(WARN)) mNavigationController.clearSslPreferences(); 2539 } 2540 2541 /** 2542 * Method to return all hit test values relevant to public WebView API. 2543 * Note that this expose more data than needed for WebView.getHitTestResult. 2544 * Unsafely returning reference to mutable internal object to avoid excessive 2545 * garbage allocation on repeated calls. 2546 */ getLastHitTestResult()2547 public HitTestData getLastHitTestResult() { 2548 if (TRACE) Log.i(TAG, "%s getLastHitTestResult", this); 2549 if (isDestroyed(WARN)) return null; 2550 AwContentsJni.get().updateLastHitTestData(mNativeAwContents, AwContents.this); 2551 return mPossiblyStaleHitTestData; 2552 } 2553 2554 /** 2555 * @see android.webkit.WebView#requestFocusNodeHref() 2556 */ requestFocusNodeHref(Message msg)2557 public void requestFocusNodeHref(Message msg) { 2558 if (TRACE) Log.i(TAG, "%s requestFocusNodeHref", this); 2559 if (msg == null || isDestroyed(WARN)) return; 2560 2561 AwContentsJni.get().updateLastHitTestData(mNativeAwContents, AwContents.this); 2562 Bundle data = msg.getData(); 2563 2564 // In order to maintain compatibility with the old WebView's implementation, 2565 // the absolute (full) url is passed in the |url| field, not only the href attribute. 2566 data.putString("url", mPossiblyStaleHitTestData.href); 2567 data.putString("title", mPossiblyStaleHitTestData.anchorText); 2568 data.putString("src", mPossiblyStaleHitTestData.imgSrc); 2569 msg.setData(data); 2570 msg.sendToTarget(); 2571 } 2572 2573 /** 2574 * @see android.webkit.WebView#requestImageRef() 2575 */ requestImageRef(Message msg)2576 public void requestImageRef(Message msg) { 2577 if (TRACE) Log.i(TAG, "%s requestImageRef", this); 2578 if (msg == null || isDestroyed(WARN)) return; 2579 2580 AwContentsJni.get().updateLastHitTestData(mNativeAwContents, AwContents.this); 2581 Bundle data = msg.getData(); 2582 data.putString("url", mPossiblyStaleHitTestData.imgSrc); 2583 msg.setData(data); 2584 msg.sendToTarget(); 2585 } 2586 2587 @VisibleForTesting getPageScaleFactor()2588 public float getPageScaleFactor() { 2589 return mPageScaleFactor; 2590 } 2591 getDeviceScaleFactor()2592 private float getDeviceScaleFactor() { 2593 return mWindowAndroid.getWindowAndroid().getDisplay().getDipScale(); 2594 } 2595 2596 /** 2597 * Add a JavaScript snippet that will run after the document has been created, but before any 2598 * script in the document executes. Note that calling this method multiple times will add 2599 * multiple scripts. Added scripts will take effect from the next navigation. If want to remove 2600 * previously set script, use the returned ScriptReference object to do so. Any JavaScript 2601 * objects injected by addWebMessageListener() or addJavascriptInterface() will be available to 2602 * use in this script. Scripts can be removed using the ScriptReference object returned when 2603 * they were added. The DOM tree may not be ready at the moment that the script runs. 2604 * 2605 * If multiple scripts are added, they will be executed in the same order they were added. 2606 * 2607 * @param script The JavaScript snippet to be run. 2608 * @param allowedOriginRules The JavaScript snippet will run on every frame whose origin matches 2609 * any one of the allowedOriginRules. 2610 * 2611 * @throws IllegalArgumentException if one of the allowedOriginRules is invalid or one of 2612 * jsObjectName and allowedOriginRules is {@code null}. 2613 * @return A {@link ScriptReference} for removing the script. 2614 */ addDocumentStartJavaScript( @onNull String script, @NonNull String[] allowedOriginRules)2615 public ScriptReference addDocumentStartJavaScript( 2616 @NonNull String script, @NonNull String[] allowedOriginRules) { 2617 if (script == null) { 2618 throw new IllegalArgumentException("script shouldn't be null."); 2619 } 2620 2621 for (int i = 0; i < allowedOriginRules.length; ++i) { 2622 if (TextUtils.isEmpty(allowedOriginRules[i])) { 2623 throw new IllegalArgumentException( 2624 "allowedOriginRules[" + i + "] shouldn't be null or empty"); 2625 } 2626 } 2627 2628 return new ScriptReference(AwContents.this, 2629 AwContentsJni.get().addDocumentStartJavaScript( 2630 mNativeAwContents, AwContents.this, script, allowedOriginRules)); 2631 } 2632 removeDocumentStartJavaScript(int scriptId)2633 /* package */ void removeDocumentStartJavaScript(int scriptId) { 2634 AwContentsJni.get().removeDocumentStartJavaScript( 2635 mNativeAwContents, AwContents.this, scriptId); 2636 } 2637 2638 /** 2639 * Add the {@link WebMessageListener} to AwContents, it will also inject the JavaScript object 2640 * with the given name to frames that have origins matching the allowedOriginRules. Note that 2641 * this call will not inject the JS object immediately. The JS object will be injected only for 2642 * future navigations (in DidClearWindowObject). 2643 * 2644 * @param jsObjectName The name for the injected JavaScript object for this {@link 2645 * WebMessageListener}. 2646 * @param allowedOrigins A list of matching rules for the allowed origins. 2647 * The JavaScript object will be injected when the frame's origin matches 2648 * any one of the allowed origins. If a wildcard "*" is provided, it will 2649 * inject JavaScript object to all frames. 2650 * @param listener The {@link WebMessageListener} to be called when received 2651 * onPostMessage(). 2652 * @throws IllegalArgumentException if one of the allowedOriginRules is invalid or one of 2653 * jsObjectName and allowedOriginRules is {@code null}. 2654 * @throws NullPointerException if listener is {@code null}. 2655 */ addWebMessageListener(@onNull String jsObjectName, @NonNull String[] allowedOriginRules, @NonNull WebMessageListener listener)2656 public void addWebMessageListener(@NonNull String jsObjectName, 2657 @NonNull String[] allowedOriginRules, @NonNull WebMessageListener listener) { 2658 if (listener == null) { 2659 throw new NullPointerException("listener shouldn't be null"); 2660 } 2661 2662 if (TextUtils.isEmpty(jsObjectName)) { 2663 throw new IllegalArgumentException("jsObjectName shouldn't be null or empty string"); 2664 } 2665 2666 for (int i = 0; i < allowedOriginRules.length; ++i) { 2667 if (TextUtils.isEmpty(allowedOriginRules[i])) { 2668 throw new IllegalArgumentException( 2669 "allowedOriginRules[" + i + "] is null or empty"); 2670 } 2671 } 2672 2673 final String exceptionMessage = 2674 AwContentsJni.get().addWebMessageListener(mNativeAwContents, AwContents.this, 2675 new WebMessageListenerHolder(listener), jsObjectName, allowedOriginRules); 2676 2677 if (!TextUtils.isEmpty(exceptionMessage)) { 2678 throw new IllegalArgumentException(exceptionMessage); 2679 } 2680 } 2681 2682 /** 2683 * Removes the {@link WebMessageListener} added by {@link addWebMessageListener}. This call will 2684 * immediately remove the JavaScript object/WebMessageListener mapping pair. So any messages 2685 * from the JavaScript object will be dropped. However the JavaScript object will only be 2686 * removed for future navigations. 2687 * 2688 * @param listener The {@link WebMessageListener} to be removed. Can not be {@code null}. 2689 */ removeWebMessageListener(@onNull String jsObjectName)2690 public void removeWebMessageListener(@NonNull String jsObjectName) { 2691 AwContentsJni.get().removeWebMessageListener( 2692 mNativeAwContents, AwContents.this, jsObjectName); 2693 } 2694 2695 /** 2696 * @see android.webkit.WebView#getScale() 2697 * 2698 * Please note that the scale returned is the page scale multiplied by 2699 * the screen density factor. See CTS WebViewTest.testSetInitialScale. 2700 */ getScale()2701 public float getScale() { 2702 if (isDestroyed(WARN)) return 1; 2703 return mPageScaleFactor * getDeviceScaleFactor(); 2704 } 2705 2706 /** 2707 * @see android.webkit.WebView#flingScroll(int, int) 2708 */ flingScroll(int velocityX, int velocityY)2709 public void flingScroll(int velocityX, int velocityY) { 2710 if (TRACE) Log.i(TAG, "%s flingScroll", this); 2711 if (isDestroyed(WARN)) return; 2712 mWebContents.getEventForwarder().startFling( 2713 SystemClock.uptimeMillis(), -velocityX, -velocityY, false, true); 2714 } 2715 2716 /** 2717 * @see android.webkit.WebView#pageUp(boolean) 2718 */ pageUp(boolean top)2719 public boolean pageUp(boolean top) { 2720 if (TRACE) Log.i(TAG, "%s pageUp", this); 2721 if (isDestroyed(WARN)) return false; 2722 return mScrollOffsetManager.pageUp(top); 2723 } 2724 2725 /** 2726 * @see android.webkit.WebView#pageDown(boolean) 2727 */ pageDown(boolean bottom)2728 public boolean pageDown(boolean bottom) { 2729 if (TRACE) Log.i(TAG, "%s pageDown", this); 2730 if (isDestroyed(WARN)) return false; 2731 return mScrollOffsetManager.pageDown(bottom); 2732 } 2733 2734 /** 2735 * @see android.webkit.WebView#canZoomIn() 2736 */ 2737 // This method uses the term 'zoom' for legacy reasons, but relates 2738 // to what chrome calls the 'page scale factor'. canZoomIn()2739 public boolean canZoomIn() { 2740 if (isDestroyed(WARN)) return false; 2741 final float zoomInExtent = mMaxPageScaleFactor - mPageScaleFactor; 2742 return zoomInExtent > ZOOM_CONTROLS_EPSILON; 2743 } 2744 2745 /** 2746 * @see android.webkit.WebView#canZoomOut() 2747 */ 2748 // This method uses the term 'zoom' for legacy reasons, but relates 2749 // to what chrome calls the 'page scale factor'. canZoomOut()2750 public boolean canZoomOut() { 2751 if (isDestroyed(WARN)) return false; 2752 final float zoomOutExtent = mPageScaleFactor - mMinPageScaleFactor; 2753 return zoomOutExtent > ZOOM_CONTROLS_EPSILON; 2754 } 2755 2756 /** 2757 * @see android.webkit.WebView#zoomIn() 2758 */ 2759 // This method uses the term 'zoom' for legacy reasons, but relates 2760 // to what chrome calls the 'page scale factor'. zoomIn()2761 public boolean zoomIn() { 2762 if (!canZoomIn()) { 2763 return false; 2764 } 2765 zoomBy(1.25f); 2766 return true; 2767 } 2768 2769 /** 2770 * @see android.webkit.WebView#zoomOut() 2771 */ 2772 // This method uses the term 'zoom' for legacy reasons, but relates 2773 // to what chrome calls the 'page scale factor'. zoomOut()2774 public boolean zoomOut() { 2775 if (!canZoomOut()) { 2776 return false; 2777 } 2778 zoomBy(0.8f); 2779 return true; 2780 } 2781 2782 /** 2783 * @see android.webkit.WebView#zoomBy() 2784 */ 2785 // This method uses the term 'zoom' for legacy reasons, but relates 2786 // to what chrome calls the 'page scale factor'. zoomBy(float delta)2787 public void zoomBy(float delta) { 2788 if (isDestroyed(WARN)) return; 2789 if (delta < 0.01f || delta > 100.0f) { 2790 throw new IllegalStateException("zoom delta value outside [0.01, 100] range."); 2791 } 2792 AwContentsJni.get().zoomBy(mNativeAwContents, AwContents.this, delta); 2793 } 2794 2795 /** 2796 * @see android.webkit.WebView#invokeZoomPicker() 2797 */ invokeZoomPicker()2798 public void invokeZoomPicker() { 2799 if (TRACE) Log.i(TAG, "%s invokeZoomPicker", this); 2800 if (!isDestroyed(WARN)) mZoomControls.invokeZoomPicker(); 2801 } 2802 2803 /** 2804 * @see android.webkit.WebView#preauthorizePermission(Uri, long) 2805 */ preauthorizePermission(Uri origin, long resources)2806 public void preauthorizePermission(Uri origin, long resources) { 2807 if (isDestroyed(NO_WARN)) return; 2808 AwContentsJni.get().preauthorizePermission( 2809 mNativeAwContents, AwContents.this, origin.toString(), resources); 2810 } 2811 2812 /** 2813 * @see WebContents.evaluateJavaScript(String, JavaScriptCallback) 2814 */ evaluateJavaScript(String script, final Callback<String> callback)2815 public void evaluateJavaScript(String script, final Callback<String> callback) { 2816 if (TRACE) Log.i(TAG, "%s evaluateJavascript=%s", this, script); 2817 if (isDestroyed(WARN)) return; 2818 JavaScriptCallback jsCallback = null; 2819 if (callback != null) { 2820 jsCallback = jsonResult -> { 2821 // Post the application callback back to the current thread to ensure the 2822 // application callback is executed without any native code on the stack. This 2823 // so that any exception thrown by the application callback won't have to be 2824 // propagated through a native call stack. 2825 AwThreadUtils.postToCurrentLooper(callback.bind(jsonResult)); 2826 }; 2827 } 2828 2829 mWebContents.evaluateJavaScript(script, jsCallback); 2830 } 2831 evaluateJavaScriptForTests(String script, final Callback<String> callback)2832 public void evaluateJavaScriptForTests(String script, final Callback<String> callback) { 2833 if (TRACE) Log.i(TAG, "%s evaluateJavascriptForTests=%s", this, script); 2834 if (isDestroyed(NO_WARN)) return; 2835 JavaScriptCallback jsCallback = null; 2836 if (callback != null) { 2837 jsCallback = jsonResult -> callback.onResult(jsonResult); 2838 } 2839 2840 mWebContents.evaluateJavaScriptForTests(script, jsCallback); 2841 } 2842 2843 /** 2844 * Send a MessageEvent to main frame. 2845 * 2846 * @param message The String message for the JavaScript MessageEvent. 2847 * @param targetOrigin The expected target frame's origin. 2848 * @param sentPorts ports for the JavaScript MessageEvent. 2849 */ postMessageToMainFrame( String message, String targetOrigin, MessagePort[] sentPorts)2850 public void postMessageToMainFrame( 2851 String message, String targetOrigin, MessagePort[] sentPorts) { 2852 if (isDestroyed(WARN)) return; 2853 2854 RenderFrameHost mainFrame = mWebContents.getMainFrame(); 2855 // If the RenderFrameHost or the RenderFrame doesn't exist we couldn't post the message. 2856 if (mainFrame == null || !mainFrame.isRenderFrameCreated()) return; 2857 2858 mWebContents.postMessageToMainFrame(message, null, targetOrigin, sentPorts); 2859 } 2860 2861 /** 2862 * Creates a message channel and returns the ports for each end of the channel. 2863 */ createMessageChannel()2864 public MessagePort[] createMessageChannel() { 2865 if (TRACE) Log.i(TAG, "%s createMessageChannel", this); 2866 if (isDestroyed(WARN)) return null; 2867 return MessagePort.createPair(); 2868 } 2869 hasAccessedInitialDocument()2870 public boolean hasAccessedInitialDocument() { 2871 if (isDestroyed(NO_WARN)) return false; 2872 return mWebContents.hasAccessedInitialDocument(); 2873 } 2874 getWebContentsAccessibility()2875 private WebContentsAccessibility getWebContentsAccessibility() { 2876 return WebContentsAccessibility.fromWebContents(mWebContents); 2877 } 2878 2879 @TargetApi(Build.VERSION_CODES.M) onProvideVirtualStructure(ViewStructure structure)2880 public void onProvideVirtualStructure(ViewStructure structure) { 2881 if (isDestroyed(WARN)) return; 2882 if (!mWebContentsObserver.didEverCommitNavigation()) { 2883 // TODO(sgurun) write a test case for this condition crbug/605251 2884 structure.setChildCount(0); 2885 return; 2886 } 2887 // for webview, the platform already calculates the scroll (as it is a view) in 2888 // ViewStructure tree. Do not offset for it in the snapshop x,y position calculations. 2889 getWebContentsAccessibility().onProvideVirtualStructure(structure, true); 2890 } 2891 onProvideAutoFillVirtualStructure(ViewStructure structure, int flags)2892 public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) { 2893 if (mAutofillProvider != null) { 2894 mAutofillProvider.onProvideAutoFillVirtualStructure(structure, flags); 2895 } 2896 } 2897 autofill(final SparseArray<AutofillValue> values)2898 public void autofill(final SparseArray<AutofillValue> values) { 2899 if (mAutofillProvider != null) { 2900 mAutofillProvider.autofill(values); 2901 } 2902 } 2903 isSelectActionModeAllowed(int actionModeItem)2904 public boolean isSelectActionModeAllowed(int actionModeItem) { 2905 return (mSettings.getDisabledActionModeMenuItems() & actionModeItem) != actionModeItem; 2906 } 2907 2908 //-------------------------------------------------------------------------------------------- 2909 // View and ViewGroup method implementations 2910 //-------------------------------------------------------------------------------------------- 2911 /** 2912 * Calls android.view.View#startActivityForResult. A RuntimeException will 2913 * be thrown by Android framework if startActivityForResult is called with 2914 * a non-Activity context. 2915 */ startActivityForResult(Intent intent, int requestCode)2916 void startActivityForResult(Intent intent, int requestCode) { 2917 // Even in fullscreen mode, startActivityForResult will still use the 2918 // initial internal access delegate because it has access to 2919 // the hidden API View#startActivityForResult. 2920 mFullScreenTransitionsState.getInitialInternalAccessDelegate() 2921 .super_startActivityForResult(intent, requestCode); 2922 } 2923 startProcessTextIntent(Intent intent)2924 void startProcessTextIntent(Intent intent) { 2925 // on Android M, WebView is not able to replace the text with the processed text. 2926 // So set the readonly flag for M. 2927 if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) { 2928 intent.putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, true); 2929 } 2930 2931 if (ContextUtils.activityFromContext(mContext) == null) { 2932 mContext.startActivity(intent); 2933 return; 2934 } 2935 2936 startActivityForResult(intent, PROCESS_TEXT_REQUEST_CODE); 2937 } 2938 onActivityResult(int requestCode, int resultCode, Intent data)2939 public void onActivityResult(int requestCode, int resultCode, Intent data) { 2940 if (isDestroyed(NO_WARN)) return; 2941 if (requestCode == PROCESS_TEXT_REQUEST_CODE) { 2942 SelectionPopupController.fromWebContents(mWebContents) 2943 .onReceivedProcessTextResult(resultCode, data); 2944 } else { 2945 Log.e(TAG, "Received activity result for an unknown request code %d", requestCode); 2946 } 2947 } 2948 2949 /** 2950 * @see android.webkit.View#onTouchEvent() 2951 */ onTouchEvent(MotionEvent event)2952 public boolean onTouchEvent(MotionEvent event) { 2953 return mAwViewMethods.onTouchEvent(event); 2954 } 2955 2956 /** 2957 * @see android.view.View#onHoverEvent() 2958 */ onHoverEvent(MotionEvent event)2959 public boolean onHoverEvent(MotionEvent event) { 2960 return mAwViewMethods.onHoverEvent(event); 2961 } 2962 2963 /** 2964 * @see android.view.View#onGenericMotionEvent() 2965 */ onGenericMotionEvent(MotionEvent event)2966 public boolean onGenericMotionEvent(MotionEvent event) { 2967 return isDestroyed(NO_WARN) ? false : mAwViewMethods.onGenericMotionEvent(event); 2968 } 2969 2970 /** 2971 * @see android.view.View#onConfigurationChanged() 2972 */ onConfigurationChanged(Configuration newConfig)2973 public void onConfigurationChanged(Configuration newConfig) { 2974 mAwViewMethods.onConfigurationChanged(newConfig); 2975 } 2976 2977 /** 2978 * @see android.view.View#onAttachedToWindow() 2979 */ onAttachedToWindow()2980 public void onAttachedToWindow() { 2981 if (TRACE) Log.i(TAG, "%s onAttachedToWindow", this); 2982 mTemporarilyDetached = false; 2983 mAwViewMethods.onAttachedToWindow(); 2984 mWindowAndroid.getWindowAndroid().getDisplay().addObserver(mDisplayObserver); 2985 2986 if (AwFeatureList.isEnabled(AwFeatures.WEBVIEW_MEASURE_SCREEN_COVERAGE)) { 2987 AwOnPreDrawListener listener = getOrCreateOnPreDrawListener(mContainerView); 2988 listener.trackContents(this); 2989 } 2990 } 2991 getOrCreateOnPreDrawListener(ViewGroup viewGroup)2992 private AwOnPreDrawListener getOrCreateOnPreDrawListener(ViewGroup viewGroup) { 2993 AwOnPreDrawListener listener = null; 2994 View rootView = viewGroup.getRootView(); 2995 if (!sRootViewPreDrawListeners.containsKey(rootView)) { 2996 if (TRACE) Log.i(TAG, "%s installing OnPreDraw Listener for %s", this, rootView); 2997 2998 ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver(); 2999 listener = new AwOnPreDrawListener(rootView); 3000 viewTreeObserver.addOnPreDrawListener(listener); 3001 sRootViewPreDrawListeners.put(rootView, listener); 3002 } else { 3003 listener = sRootViewPreDrawListeners.get(rootView); 3004 } 3005 return listener; 3006 } 3007 detachPreDrawListener()3008 private void detachPreDrawListener() { 3009 // Don't use getOrCreateOnPreDrawListener, if we have to create 3010 // the listener at this point then something has gone wrong and 3011 // we should fail rather than create a new one. 3012 AwOnPreDrawListener listener = sRootViewPreDrawListeners.get(mContainerView.getRootView()); 3013 if (listener == null) return; 3014 3015 listener.unTrackContents(this); 3016 if (listener.isTracking()) return; 3017 3018 if (TRACE) Log.i(TAG, "%s removing " + listener, this); 3019 sRootViewPreDrawListeners.remove(mContainerView.getRootView()); 3020 } 3021 3022 /** 3023 * @see android.view.View#onDetachedFromWindow() 3024 */ 3025 @SuppressLint("MissingSuperCall") onDetachedFromWindow()3026 public void onDetachedFromWindow() { 3027 if (TRACE) Log.i(TAG, "%s onDetachedFromWindow", this); 3028 3029 if (AwFeatureList.isEnabled(AwFeatures.WEBVIEW_MEASURE_SCREEN_COVERAGE)) { 3030 detachPreDrawListener(); 3031 } 3032 mWindowAndroid.getWindowAndroid().getDisplay().removeObserver(mDisplayObserver); 3033 mAwViewMethods.onDetachedFromWindow(); 3034 } 3035 3036 /** 3037 * @see android.view.View#onWindowFocusChanged() 3038 */ onWindowFocusChanged(boolean hasWindowFocus)3039 public void onWindowFocusChanged(boolean hasWindowFocus) { 3040 mAwViewMethods.onWindowFocusChanged(hasWindowFocus); 3041 } 3042 3043 /** 3044 * @see android.view.View#onFocusChanged() 3045 */ onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)3046 public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 3047 if (!mTemporarilyDetached) { 3048 mAwViewMethods.onFocusChanged(focused, direction, previouslyFocusedRect); 3049 } 3050 } 3051 3052 /** 3053 * @see android.view.View#onStartTemporaryDetach() 3054 */ onStartTemporaryDetach()3055 public void onStartTemporaryDetach() { 3056 mTemporarilyDetached = true; 3057 } 3058 3059 /** 3060 * @see android.view.View#onFinishTemporaryDetach() 3061 */ onFinishTemporaryDetach()3062 public void onFinishTemporaryDetach() { 3063 mTemporarilyDetached = false; 3064 } 3065 3066 /** 3067 * @see android.view.View#onSizeChanged() 3068 */ onSizeChanged(int w, int h, int ow, int oh)3069 public void onSizeChanged(int w, int h, int ow, int oh) { 3070 mAwViewMethods.onSizeChanged(w, h, ow, oh); 3071 } 3072 3073 /** 3074 * @see android.view.View#onVisibilityChanged() 3075 */ onVisibilityChanged(View changedView, int visibility)3076 public void onVisibilityChanged(View changedView, int visibility) { 3077 mAwViewMethods.onVisibilityChanged(changedView, visibility); 3078 } 3079 3080 /** 3081 * @see android.view.View#onWindowVisibilityChanged() 3082 */ onWindowVisibilityChanged(int visibility)3083 public void onWindowVisibilityChanged(int visibility) { 3084 mAwViewMethods.onWindowVisibilityChanged(visibility); 3085 } 3086 setViewVisibilityInternal(boolean visible)3087 private void setViewVisibilityInternal(boolean visible) { 3088 mIsViewVisible = visible; 3089 if (!isDestroyed(NO_WARN)) { 3090 AwContentsJni.get().setViewVisibility( 3091 mNativeAwContents, AwContents.this, mIsViewVisible); 3092 } 3093 postUpdateWebContentsVisibility(); 3094 } 3095 setWindowVisibilityInternal(boolean visible)3096 private void setWindowVisibilityInternal(boolean visible) { 3097 mInvalidateRootViewOnNextDraw |= Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP 3098 && visible && !mIsWindowVisible; 3099 mIsWindowVisible = visible; 3100 if (!isDestroyed(NO_WARN)) { 3101 AwContentsJni.get().setWindowVisibility( 3102 mNativeAwContents, AwContents.this, mIsWindowVisible); 3103 } 3104 postUpdateWebContentsVisibility(); 3105 } 3106 postUpdateWebContentsVisibility()3107 private void postUpdateWebContentsVisibility() { 3108 if (mIsUpdateVisibilityTaskPending) return; 3109 // When WebView is attached to a visible window, WebView will be 3110 // attached to a window whose visibility is initially invisible, then 3111 // the window visibility will be updated to true. This means CVC 3112 // visibility will be set to false then true immediately, in the same 3113 // function call of View#dispatchAttachedToWindow. DetachedFromWindow 3114 // is a similar case, where window visibility changes before AwContents 3115 // is detached from window. 3116 // 3117 // To prevent this flip of CVC visibility, post the task to update CVC 3118 // visibility during attach, detach and window visibility change. 3119 mIsUpdateVisibilityTaskPending = true; 3120 PostTask.postTask(UiThreadTaskTraits.DEFAULT, mUpdateVisibilityRunnable); 3121 } 3122 updateWebContentsVisibility()3123 private void updateWebContentsVisibility() { 3124 mIsUpdateVisibilityTaskPending = false; 3125 if (isDestroyed(NO_WARN)) return; 3126 boolean contentVisible = AwContentsJni.get().isVisible(mNativeAwContents, AwContents.this); 3127 3128 if (contentVisible && !mIsContentVisible) { 3129 mWebContents.onShow(); 3130 } else if (!contentVisible && mIsContentVisible) { 3131 mWebContents.onHide(); 3132 } 3133 mIsContentVisible = contentVisible; 3134 updateChildProcessImportance(); 3135 } 3136 3137 /** 3138 * Returns true if the page is visible according to DOM page visibility API. 3139 * See http://www.w3.org/TR/page-visibility/ 3140 * This method is only called by tests and will return the supposed CVC 3141 * visibility without waiting a pending mUpdateVisibilityRunnable to run. 3142 */ 3143 @VisibleForTesting isPageVisible()3144 public boolean isPageVisible() { 3145 if (isDestroyed(NO_WARN)) return mIsContentVisible; 3146 return AwContentsJni.get().isVisible(mNativeAwContents, AwContents.this); 3147 } 3148 3149 /** 3150 * Returns true if the web contents has an associated interstitial. 3151 * This method is only called by tests. 3152 */ 3153 @VisibleForTesting isDisplayingInterstitialForTesting()3154 public boolean isDisplayingInterstitialForTesting() { 3155 return AwContentsJni.get().isDisplayingInterstitialForTesting( 3156 mNativeAwContents, AwContents.this); 3157 } 3158 3159 /** 3160 * Key for opaque state in bundle. Note this is only public for tests. 3161 */ 3162 public static final String SAVE_RESTORE_STATE_KEY = "WEBVIEW_CHROMIUM_STATE"; 3163 3164 /** 3165 * Save the state of this AwContents into provided Bundle. 3166 * @return False if saving state failed. 3167 */ saveState(Bundle outState)3168 public boolean saveState(Bundle outState) { 3169 if (TRACE) Log.i(TAG, "%s saveState", this); 3170 if (isDestroyed(WARN) || outState == null) return false; 3171 3172 byte[] state = AwContentsJni.get().getOpaqueState(mNativeAwContents, AwContents.this); 3173 if (state == null) return false; 3174 3175 outState.putByteArray(SAVE_RESTORE_STATE_KEY, state); 3176 return true; 3177 } 3178 3179 /** 3180 * Restore the state of this AwContents into provided Bundle. 3181 * @param inState Must be a bundle returned by saveState. 3182 * @return False if restoring state failed. 3183 */ restoreState(Bundle inState)3184 public boolean restoreState(Bundle inState) { 3185 if (TRACE) Log.i(TAG, "%s restoreState", this); 3186 if (isDestroyed(WARN) || inState == null) return false; 3187 3188 byte[] state = inState.getByteArray(SAVE_RESTORE_STATE_KEY); 3189 if (state == null) return false; 3190 3191 boolean result = AwContentsJni.get().restoreFromOpaqueState( 3192 mNativeAwContents, AwContents.this, state); 3193 3194 // The onUpdateTitle callback normally happens when a page is loaded, 3195 // but is optimized out in the restoreState case because the title is 3196 // already restored. See WebContentsImpl::UpdateTitleForEntry. So we 3197 // call the callback explicitly here. 3198 if (result) mContentsClient.onReceivedTitle(mWebContents.getTitle()); 3199 3200 return result; 3201 } 3202 3203 /** 3204 * @see JavascriptInjector#addPossiblyUnsafeInterface(Object, String, Class) 3205 */ 3206 @SuppressLint("NewApi") // JavascriptInterface requires API level 17. addJavascriptInterface(Object object, String name)3207 public void addJavascriptInterface(Object object, String name) { 3208 if (TRACE) Log.i(TAG, "%s addJavascriptInterface=%s", this, name); 3209 if (isDestroyed(WARN)) return; 3210 Class<? extends Annotation> requiredAnnotation = null; 3211 if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 3212 requiredAnnotation = JavascriptInterface.class; 3213 } 3214 3215 getJavascriptInjector().addPossiblyUnsafeInterface(object, name, requiredAnnotation); 3216 } 3217 3218 /** 3219 * @see android.webkit.WebView#removeJavascriptInterface(String) 3220 */ removeJavascriptInterface(String interfaceName)3221 public void removeJavascriptInterface(String interfaceName) { 3222 if (TRACE) Log.i(TAG, "%s removeInterface=%s", this, interfaceName); 3223 if (isDestroyed(WARN)) return; 3224 3225 getJavascriptInjector().removeInterface(interfaceName); 3226 } 3227 3228 /** 3229 * If native accessibility (not script injection) is enabled, and if this is 3230 * running on JellyBean or later, returns an AccessibilityNodeProvider that 3231 * implements native accessibility for this view. Returns null otherwise. 3232 * @return The AccessibilityNodeProvider, if available, or null otherwise. 3233 */ getAccessibilityNodeProvider()3234 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 3235 return mAwViewMethods.getAccessibilityNodeProvider(); 3236 } 3237 supportsAccessibilityAction(int action)3238 public boolean supportsAccessibilityAction(int action) { 3239 return isDestroyed(WARN) ? false : getWebContentsAccessibility().supportsAction(action); 3240 } 3241 3242 /** 3243 * @see android.webkit.WebView#performAccessibilityAction(int, Bundle) 3244 */ performAccessibilityAction(int action, Bundle arguments)3245 public boolean performAccessibilityAction(int action, Bundle arguments) { 3246 return mAwViewMethods.performAccessibilityAction(action, arguments); 3247 } 3248 3249 /** 3250 * @see android.webkit.WebView#clearFormData(). 3251 * 3252 * The popup shall also be hidden on the WebView detached from window. 3253 */ hideAutofillPopup()3254 public void hideAutofillPopup() { 3255 if (TRACE) Log.i(TAG, "%s hideAutofillPopup", this); 3256 if (mAwAutofillClient != null) { 3257 mAwAutofillClient.hideAutofillPopup(); 3258 } 3259 if (mAutofillProvider != null) { 3260 mAutofillProvider.hidePopup(); 3261 } 3262 } 3263 setNetworkAvailable(boolean networkUp)3264 public void setNetworkAvailable(boolean networkUp) { 3265 if (TRACE) Log.i(TAG, "%s setNetworkAvailable=%s", this, networkUp); 3266 if (!isDestroyed(WARN)) { 3267 // For backward compatibility when an app uses this API disable the 3268 // Network Information API to prevent inconsistencies, 3269 // see crbug.com/520088. 3270 NetworkChangeNotifier.setAutoDetectConnectivityState(false); 3271 AwContentsJni.get().setJsOnlineProperty(mNativeAwContents, AwContents.this, networkUp); 3272 } 3273 } 3274 3275 /** 3276 * Inserts a {@link VisualStateCallback}. 3277 * 3278 * The {@link VisualStateCallback} will be inserted in Blink and will be invoked when the 3279 * contents of the DOM tree at the moment that the callback was inserted (or later) are drawn 3280 * into the screen. In other words, the following events need to happen before the callback is 3281 * invoked: 3282 * 1. The DOM tree is committed becoming the pending tree - see ThreadProxy::BeginMainFrame 3283 * 2. The pending tree is activated becoming the active tree 3284 * 3. A frame swap happens that draws the active tree into the screen 3285 * 3286 * @param requestId an id that will be returned from the callback invocation to allow 3287 * callers to match requests with callbacks. 3288 * @param callback the callback to be inserted 3289 * @throw IllegalStateException if this method is invoked after {@link #destroy()} has been 3290 * called. 3291 */ insertVisualStateCallback(long requestId, VisualStateCallback callback)3292 public void insertVisualStateCallback(long requestId, VisualStateCallback callback) { 3293 if (TRACE) Log.i(TAG, "%s insertVisualStateCallback", this); 3294 if (isDestroyed(NO_WARN)) throw new IllegalStateException( 3295 "insertVisualStateCallback cannot be called after the WebView has been destroyed"); 3296 if (callback == null) { 3297 throw new IllegalArgumentException("VisualStateCallback shouldn't be null"); 3298 } 3299 AwContentsJni.get().insertVisualStateCallback( 3300 mNativeAwContents, AwContents.this, requestId, callback); 3301 } 3302 isPopupWindow()3303 public boolean isPopupWindow() { 3304 return mIsPopupWindow; 3305 } 3306 updateChildProcessImportance()3307 private void updateChildProcessImportance() { 3308 @ChildProcessImportance 3309 int effectiveImportance = ChildProcessImportance.IMPORTANT; 3310 if (mRendererPriorityWaivedWhenNotVisible && !mIsContentVisible) { 3311 effectiveImportance = ChildProcessImportance.NORMAL; 3312 } else { 3313 switch (mRendererPriority) { 3314 case RendererPriority.INITIAL: 3315 case RendererPriority.HIGH: 3316 effectiveImportance = ChildProcessImportance.IMPORTANT; 3317 break; 3318 case RendererPriority.LOW: 3319 effectiveImportance = ChildProcessImportance.MODERATE; 3320 break; 3321 case RendererPriority.WAIVED: 3322 effectiveImportance = ChildProcessImportance.NORMAL; 3323 break; 3324 default: 3325 assert false; 3326 } 3327 } 3328 mWebContents.setImportance(effectiveImportance); 3329 } 3330 3331 @RendererPriority getRendererRequestedPriority()3332 public int getRendererRequestedPriority() { 3333 return mRendererPriority; 3334 } 3335 getRendererPriorityWaivedWhenNotVisible()3336 public boolean getRendererPriorityWaivedWhenNotVisible() { 3337 return mRendererPriorityWaivedWhenNotVisible; 3338 } 3339 setRendererPriorityPolicy( @endererPriority int rendererRequestedPriority, boolean waivedWhenNotVisible)3340 public void setRendererPriorityPolicy( 3341 @RendererPriority int rendererRequestedPriority, boolean waivedWhenNotVisible) { 3342 mRendererPriority = rendererRequestedPriority; 3343 mRendererPriorityWaivedWhenNotVisible = waivedWhenNotVisible; 3344 updateChildProcessImportance(); 3345 } 3346 setTextClassifier(TextClassifier textClassifier)3347 public void setTextClassifier(TextClassifier textClassifier) { 3348 assert mWebContents != null; 3349 SelectionPopupController.fromWebContents(mWebContents).setTextClassifier(textClassifier); 3350 } 3351 getTextClassifier()3352 public TextClassifier getTextClassifier() { 3353 assert mWebContents != null; 3354 return SelectionPopupController.fromWebContents(mWebContents).getTextClassifier(); 3355 } 3356 getRenderProcess()3357 public AwRenderProcess getRenderProcess() { 3358 if (isDestroyed(WARN)) { 3359 return null; 3360 } 3361 return AwContentsJni.get().getRenderProcess(mNativeAwContents, AwContents.this); 3362 } 3363 3364 //-------------------------------------------------------------------------------------------- 3365 // Methods called from native via JNI 3366 //-------------------------------------------------------------------------------------------- 3367 3368 @CalledByNative onDocumentHasImagesResponse(boolean result, Message message)3369 private static void onDocumentHasImagesResponse(boolean result, Message message) { 3370 message.arg1 = result ? 1 : 0; 3371 message.sendToTarget(); 3372 } 3373 3374 @CalledByNative onReceivedTouchIconUrl(String url, boolean precomposed)3375 private void onReceivedTouchIconUrl(String url, boolean precomposed) { 3376 mContentsClient.onReceivedTouchIconUrl(url, precomposed); 3377 } 3378 3379 @CalledByNative onReceivedIcon(Bitmap bitmap)3380 private void onReceivedIcon(Bitmap bitmap) { 3381 mContentsClient.onReceivedIcon(bitmap); 3382 mFavicon = bitmap; 3383 } 3384 3385 @CalledByNative onCreateTouchHandle()3386 private long onCreateTouchHandle() { 3387 PopupTouchHandleDrawable drawable = PopupTouchHandleDrawable.create( 3388 mTouchHandleDrawables, mWebContents, mContainerView); 3389 return drawable.getNativeDrawable(); 3390 } 3391 3392 /** Callback for generateMHTML. */ 3393 @CalledByNative generateMHTMLCallback(String path, long size, Callback<String> callback)3394 private static void generateMHTMLCallback(String path, long size, Callback<String> callback) { 3395 if (callback == null) return; 3396 AwThreadUtils.postToUiThreadLooper(() -> { callback.onResult(size < 0 ? null : path); }); 3397 } 3398 3399 @CalledByNative onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm)3400 private void onReceivedHttpAuthRequest(AwHttpAuthHandler handler, String host, String realm) { 3401 mContentsClient.onReceivedHttpAuthRequest(handler, host, realm); 3402 3403 AwHistogramRecorder.recordCallbackInvocation( 3404 AwHistogramRecorder.WebViewCallbackType.ON_RECEIVED_HTTP_AUTH_REQUEST); 3405 } 3406 getGeolocationPermissions()3407 public AwGeolocationPermissions getGeolocationPermissions() { 3408 return mBrowserContext.getGeolocationPermissions(); 3409 } 3410 invokeGeolocationCallback(boolean value, String requestingFrame)3411 public void invokeGeolocationCallback(boolean value, String requestingFrame) { 3412 if (isDestroyed(NO_WARN)) return; 3413 AwContentsJni.get().invokeGeolocationCallback( 3414 mNativeAwContents, AwContents.this, value, requestingFrame); 3415 } 3416 3417 @CalledByNative onGeolocationPermissionsShowPrompt(String origin)3418 private void onGeolocationPermissionsShowPrompt(String origin) { 3419 if (isDestroyed(NO_WARN)) return; 3420 AwGeolocationPermissions permissions = mBrowserContext.getGeolocationPermissions(); 3421 // Reject if geoloaction is disabled, or the origin has a retained deny 3422 if (!mSettings.getGeolocationEnabled()) { 3423 AwContentsJni.get().invokeGeolocationCallback( 3424 mNativeAwContents, AwContents.this, false, origin); 3425 return; 3426 } 3427 // Allow if the origin has a retained allow 3428 if (permissions.hasOrigin(origin)) { 3429 AwContentsJni.get().invokeGeolocationCallback(mNativeAwContents, AwContents.this, 3430 permissions.isOriginAllowed(origin), origin); 3431 return; 3432 } 3433 mContentsClient.onGeolocationPermissionsShowPrompt( 3434 origin, new AwGeolocationCallback(origin, this)); 3435 } 3436 3437 @CalledByNative onGeolocationPermissionsHidePrompt()3438 private void onGeolocationPermissionsHidePrompt() { 3439 mContentsClient.onGeolocationPermissionsHidePrompt(); 3440 } 3441 3442 @CalledByNative onPermissionRequest(AwPermissionRequest awPermissionRequest)3443 private void onPermissionRequest(AwPermissionRequest awPermissionRequest) { 3444 mContentsClient.onPermissionRequest(awPermissionRequest); 3445 } 3446 3447 @CalledByNative onPermissionRequestCanceled(AwPermissionRequest awPermissionRequest)3448 private void onPermissionRequestCanceled(AwPermissionRequest awPermissionRequest) { 3449 mContentsClient.onPermissionRequestCanceled(awPermissionRequest); 3450 } 3451 3452 @CalledByNative onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting)3453 public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, 3454 boolean isDoneCounting) { 3455 mContentsClient.onFindResultReceived(activeMatchOrdinal, numberOfMatches, isDoneCounting); 3456 } 3457 3458 @CalledByNative onNewPicture()3459 public void onNewPicture() { 3460 // Don't call capturePicture() here but instead defer it until the posted task runs within 3461 // the callback helper, to avoid doubling back into the renderer compositor in the middle 3462 // of the notification it is sending up to here. 3463 mContentsClient.getCallbackHelper().postOnNewPicture(mPictureListenerContentProvider); 3464 } 3465 3466 /** 3467 * Invokes the given {@link VisualStateCallback}. 3468 * 3469 * @param callback the callback to be invoked 3470 * @param requestId the id passed to {@link AwContents#insertVisualStateCallback} 3471 * @param result true if the callback should succeed and false otherwise 3472 */ 3473 @CalledByNative invokeVisualStateCallback( final VisualStateCallback callback, final long requestId)3474 public void invokeVisualStateCallback( 3475 final VisualStateCallback callback, final long requestId) { 3476 if (isDestroyed(NO_WARN)) return; 3477 // Posting avoids invoking the callback inside invoking_composite_ 3478 // (see synchronous_compositor_impl.cc and crbug/452530). 3479 AwThreadUtils.postToUiThreadLooper(() -> callback.onComplete(requestId)); 3480 } 3481 3482 // Called as a result of AwContentsJni.get().updateLastHitTestData. 3483 @CalledByNative updateHitTestData( int type, String extra, String href, String anchorText, String imgSrc)3484 private void updateHitTestData( 3485 int type, String extra, String href, String anchorText, String imgSrc) { 3486 mPossiblyStaleHitTestData.hitTestResultType = type; 3487 mPossiblyStaleHitTestData.hitTestResultExtraData = extra; 3488 mPossiblyStaleHitTestData.href = href; 3489 mPossiblyStaleHitTestData.anchorText = anchorText; 3490 mPossiblyStaleHitTestData.imgSrc = imgSrc; 3491 } 3492 3493 @CalledByNative postInvalidateOnAnimation()3494 private void postInvalidateOnAnimation() { 3495 if (!VSyncMonitor.isInsideVSync()) { 3496 mContainerView.postInvalidateOnAnimation(); 3497 } else { 3498 mContainerView.invalidate(); 3499 } 3500 } 3501 3502 @CalledByNative getLocationOnScreen()3503 private int[] getLocationOnScreen() { 3504 int[] result = new int[2]; 3505 mContainerView.getLocationOnScreen(result); 3506 return result; 3507 } 3508 3509 @CalledByNative onWebLayoutPageScaleFactorChanged(float webLayoutPageScaleFactor)3510 private void onWebLayoutPageScaleFactorChanged(float webLayoutPageScaleFactor) { 3511 // This change notification comes from the renderer thread, not from the cc/ impl thread. 3512 mLayoutSizer.onPageScaleChanged(webLayoutPageScaleFactor); 3513 } 3514 3515 @CalledByNative onWebLayoutContentsSizeChanged(int widthCss, int heightCss)3516 private void onWebLayoutContentsSizeChanged(int widthCss, int heightCss) { 3517 // This change notification comes from the renderer thread, not from the cc/ impl thread. 3518 mLayoutSizer.onContentSizeChanged(widthCss, heightCss); 3519 } 3520 3521 @CalledByNative scrollContainerViewTo(int xPx, int yPx)3522 private void scrollContainerViewTo(int xPx, int yPx) { 3523 // Both xPx and yPx are in physical pixel scale. 3524 mScrollOffsetManager.scrollContainerViewTo(xPx, yPx); 3525 } 3526 3527 @CalledByNative updateScrollState(int maxContainerViewScrollOffsetX, int maxContainerViewScrollOffsetY, float contentWidthDip, float contentHeightDip, float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor)3528 private void updateScrollState(int maxContainerViewScrollOffsetX, 3529 int maxContainerViewScrollOffsetY, float contentWidthDip, float contentHeightDip, 3530 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) { 3531 mContentWidthDip = contentWidthDip; 3532 mContentHeightDip = contentHeightDip; 3533 mScrollOffsetManager.setMaxScrollOffset(maxContainerViewScrollOffsetX, 3534 maxContainerViewScrollOffsetY); 3535 setPageScaleFactorAndLimits(pageScaleFactor, minPageScaleFactor, maxPageScaleFactor); 3536 } 3537 3538 @CalledByNative setAwAutofillClient(AwAutofillClient client)3539 private void setAwAutofillClient(AwAutofillClient client) { 3540 mAwAutofillClient = client; 3541 client.init(mContext); 3542 } 3543 3544 @CalledByNative didOverscroll(int deltaX, int deltaY, float velocityX, float velocityY)3545 private void didOverscroll(int deltaX, int deltaY, float velocityX, float velocityY) { 3546 mScrollOffsetManager.overScrollBy(deltaX, deltaY); 3547 3548 if (mOverScrollGlow == null) return; 3549 3550 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY); 3551 final int oldX = mContainerView.getScrollX(); 3552 final int oldY = mContainerView.getScrollY(); 3553 final int x = oldX + deltaX; 3554 final int y = oldY + deltaY; 3555 final int scrollRangeX = mScrollOffsetManager.computeMaximumHorizontalScrollOffset(); 3556 final int scrollRangeY = mScrollOffsetManager.computeMaximumVerticalScrollOffset(); 3557 // absorbGlow() will release the glow if it is not finished. 3558 mOverScrollGlow.absorbGlow(x, y, oldX, oldY, scrollRangeX, scrollRangeY, 3559 (float) Math.hypot(velocityX, velocityY)); 3560 3561 if (mOverScrollGlow.isAnimating()) { 3562 postInvalidateOnAnimation(); 3563 } 3564 } 3565 3566 /** 3567 * Determine if at least one edge of the WebView extends over the edge of the window. 3568 */ extendsOutOfWindow()3569 private boolean extendsOutOfWindow() { 3570 int loc[] = new int[2]; 3571 mContainerView.getLocationOnScreen(loc); 3572 int x = loc[0]; 3573 int y = loc[1]; 3574 mContainerView.getRootView().getLocationOnScreen(loc); 3575 int rootX = loc[0]; 3576 int rootY = loc[1]; 3577 3578 // Get the position of the current view, relative to its root view 3579 int relativeX = x - rootX; 3580 int relativeY = y - rootY; 3581 3582 if (relativeX < 0 || relativeY < 0 3583 || relativeX + mContainerView.getWidth() > mContainerView.getRootView().getWidth() 3584 || relativeY + mContainerView.getHeight() 3585 > mContainerView.getRootView().getHeight()) { 3586 return true; 3587 } 3588 return false; 3589 } 3590 3591 /** 3592 * Determine if it's reasonable to show any sort of interstitial. If the WebView is not visible, 3593 * the user may not be able to interact with the UI. 3594 * @return true if the WebView is visible 3595 */ 3596 @VisibleForTesting 3597 @CalledByNative canShowInterstitial()3598 protected boolean canShowInterstitial() { 3599 return mIsAttachedToWindow && mIsViewVisible; 3600 } 3601 3602 @CalledByNative getErrorUiType()3603 private int getErrorUiType() { 3604 if (extendsOutOfWindow()) { 3605 return ErrorUiType.QUIET_GIANT; 3606 } else if (canShowBigInterstitial()) { 3607 return ErrorUiType.LOUD; 3608 } else { 3609 return ErrorUiType.QUIET_SMALL; 3610 } 3611 } 3612 3613 /** 3614 * Determine if it's suitable to show the interstitial for browsers and main UIs. If the WebView 3615 * is close to full-screen, we assume the app is using it as the main UI, so we show the same 3616 * interstitial Chrome uses. Otherwise, we assume the WebView is part of a larger composed page, 3617 * and will show a different interstitial. 3618 * @return true if the WebView should display the large interstitial 3619 */ 3620 @VisibleForTesting canShowBigInterstitial()3621 protected boolean canShowBigInterstitial() { 3622 double percentOfScreenHeight = 3623 (double) mContainerView.getHeight() / mContainerView.getRootView().getHeight(); 3624 3625 // Make a guess as to whether the WebView is the predominant part of the UI 3626 return mContainerView.getWidth() == mContainerView.getRootView().getWidth() 3627 && percentOfScreenHeight >= MIN_SCREEN_HEIGHT_PERCENTAGE_FOR_INTERSTITIAL; 3628 } 3629 3630 /** 3631 * Return the device locale in the same format we use to populate the 'hl' query parameter for 3632 * Safe Browsing interstitial urls, as done in BaseUIManager::app_locale(). 3633 */ 3634 @VisibleForTesting getSafeBrowsingLocaleForTesting()3635 public static String getSafeBrowsingLocaleForTesting() { 3636 return AwContentsJni.get().getSafeBrowsingLocaleForTesting(); 3637 } 3638 3639 // ------------------------------------------------------------------------------------------- 3640 // Helper methods 3641 // ------------------------------------------------------------------------------------------- 3642 setPageScaleFactorAndLimits( float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor)3643 private void setPageScaleFactorAndLimits( 3644 float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) { 3645 if (mPageScaleFactor == pageScaleFactor 3646 && mMinPageScaleFactor == minPageScaleFactor 3647 && mMaxPageScaleFactor == maxPageScaleFactor) { 3648 return; 3649 } 3650 mMinPageScaleFactor = minPageScaleFactor; 3651 mMaxPageScaleFactor = maxPageScaleFactor; 3652 if (mPageScaleFactor != pageScaleFactor) { 3653 float oldPageScaleFactor = mPageScaleFactor; 3654 mPageScaleFactor = pageScaleFactor; 3655 float dipScale = getDeviceScaleFactor(); 3656 mContentsClient.getCallbackHelper().postOnScaleChangedScaled( 3657 oldPageScaleFactor * dipScale, mPageScaleFactor * dipScale); 3658 } 3659 mZoomControls.updateZoomControls(); 3660 } 3661 saveWebArchiveInternal(String path, final Callback<String> callback)3662 private void saveWebArchiveInternal(String path, final Callback<String> callback) { 3663 if (path == null || isDestroyed(WARN)) { 3664 if (callback == null) return; 3665 3666 AwThreadUtils.postToUiThreadLooper(callback.bind(null)); 3667 } else { 3668 AwContentsJni.get().generateMHTML(mNativeAwContents, AwContents.this, path, callback); 3669 } 3670 } 3671 3672 /** 3673 * Try to generate a pathname for saving an MHTML archive. This roughly follows WebView's 3674 * autoname logic. 3675 */ generateArchiveAutoNamePath(String originalUrl, String baseName)3676 private static String generateArchiveAutoNamePath(String originalUrl, String baseName) { 3677 String name = null; 3678 if (originalUrl != null && !originalUrl.isEmpty()) { 3679 try { 3680 String path = new URL(originalUrl).getPath(); 3681 int lastSlash = path.lastIndexOf('/'); 3682 if (lastSlash > 0) { 3683 name = path.substring(lastSlash + 1); 3684 } else { 3685 name = path; 3686 } 3687 } catch (MalformedURLException e) { 3688 // If it fails parsing the URL, we'll just rely on the default name below. 3689 } 3690 } 3691 3692 if (TextUtils.isEmpty(name)) name = "index"; 3693 3694 String testName = baseName + name + WEB_ARCHIVE_EXTENSION; 3695 if (!new File(testName).exists()) return testName; 3696 3697 for (int i = 1; i < 100; i++) { 3698 testName = baseName + name + "-" + i + WEB_ARCHIVE_EXTENSION; 3699 if (!new File(testName).exists()) return testName; 3700 } 3701 3702 Log.e(TAG, "Unable to auto generate archive name for path: %s", baseName); 3703 return null; 3704 } 3705 3706 @Override extractSmartClipData(int x, int y, int width, int height)3707 public void extractSmartClipData(int x, int y, int width, int height) { 3708 if (!isDestroyed(WARN)) { 3709 mWebContents.requestSmartClipExtract(x, y, width, height); 3710 } 3711 } 3712 3713 @Override setSmartClipResultHandler(final Handler resultHandler)3714 public void setSmartClipResultHandler(final Handler resultHandler) { 3715 if (isDestroyed(WARN)) return; 3716 3717 mWebContents.setSmartClipResultHandler(resultHandler); 3718 } 3719 insertVisualStateCallbackIfNotDestroyed( long requestId, VisualStateCallback callback)3720 protected void insertVisualStateCallbackIfNotDestroyed( 3721 long requestId, VisualStateCallback callback) { 3722 if (TRACE) Log.i(TAG, "%s insertVisualStateCallbackIfNotDestroyed", this); 3723 if (isDestroyed(NO_WARN)) return; 3724 AwContentsJni.get().insertVisualStateCallback( 3725 mNativeAwContents, AwContents.this, requestId, callback); 3726 } 3727 isDpadEvent(KeyEvent event)3728 public static boolean isDpadEvent(KeyEvent event) { 3729 if (event.getAction() == KeyEvent.ACTION_DOWN) { 3730 switch (event.getKeyCode()) { 3731 case KeyEvent.KEYCODE_DPAD_CENTER: 3732 case KeyEvent.KEYCODE_DPAD_DOWN: 3733 case KeyEvent.KEYCODE_DPAD_UP: 3734 case KeyEvent.KEYCODE_DPAD_LEFT: 3735 case KeyEvent.KEYCODE_DPAD_RIGHT: 3736 return true; 3737 } 3738 } 3739 return false; 3740 } 3741 3742 // -------------------------------------------------------------------------------------------- 3743 // This is the AwViewMethods implementation that does real work. The AwViewMethodsImpl is 3744 // hooked up to the WebView in embedded mode and to the FullScreenView in fullscreen mode, 3745 // but not to both at the same time. 3746 private class AwViewMethodsImpl implements AwViewMethods { 3747 private int mLayerType = View.LAYER_TYPE_NONE; 3748 private ComponentCallbacks2 mComponentCallbacks; 3749 3750 // Only valid within software onDraw(). 3751 private final Rect mClipBoundsTemporary = new Rect(); 3752 3753 @SuppressLint("DrawAllocation") // For new AwFunctor. 3754 @Override onDraw(Canvas canvas)3755 public void onDraw(Canvas canvas) { 3756 if (isDestroyed(NO_WARN)) { 3757 TraceEvent.instant("EarlyOut_destroyed"); 3758 canvas.drawColor(getEffectiveBackgroundColor()); 3759 return; 3760 } 3761 3762 // For hardware draws, the clip at onDraw time could be different 3763 // from the clip during DrawGL. 3764 if (!canvas.isHardwareAccelerated() && !canvas.getClipBounds(mClipBoundsTemporary)) { 3765 TraceEvent.instant("EarlyOut_software_empty_clip"); 3766 return; 3767 } 3768 3769 if (canvas.isHardwareAccelerated() && mDrawFunctor == null) { 3770 AwFunctor newFunctor; 3771 AwDrawFnImpl.DrawFnAccess drawFnAccess = 3772 mNativeDrawFunctorFactory.getDrawFnAccess(); 3773 if (drawFnAccess != null) { 3774 newFunctor = new AwDrawFnImpl(drawFnAccess); 3775 } else { 3776 newFunctor = new AwGLFunctor(mNativeDrawFunctorFactory, mContainerView); 3777 } 3778 setFunctor(newFunctor); 3779 } 3780 3781 mScrollOffsetManager.syncScrollOffsetFromOnDraw(); 3782 int scrollX = mContainerView.getScrollX(); 3783 int scrollY = mContainerView.getScrollY(); 3784 Rect globalVisibleRect = getGlobalVisibleRect(); 3785 // Workaround for bug in libhwui on N that does not swap if inserting functor is the 3786 // only operation in a canvas. See crbug.com/704212. 3787 if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N 3788 || Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) { 3789 if (mPaintForNWorkaround == null) { 3790 mPaintForNWorkaround = new Paint(); 3791 // Note a completely transparent color will get optimized out. So draw almost 3792 // transparent black, but then scale alpha down to effectively 0. 3793 mPaintForNWorkaround.setColor(Color.argb(1, 0, 0, 0)); 3794 ColorMatrix colorMatrix = new ColorMatrix(); 3795 colorMatrix.setScale(0.f, 0.f, 0.f, 0.1f); 3796 mPaintForNWorkaround.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); 3797 } 3798 canvas.drawRect(0, 0, 1, 1, mPaintForNWorkaround); 3799 } 3800 boolean did_draw = AwContentsJni.get().onDraw(mNativeAwContents, AwContents.this, 3801 canvas, canvas.isHardwareAccelerated(), scrollX, scrollY, 3802 globalVisibleRect.left, globalVisibleRect.top, globalVisibleRect.right, 3803 globalVisibleRect.bottom, ForceAuxiliaryBitmapRendering.sResult); 3804 if (canvas.isHardwareAccelerated() 3805 && AwContentsJni.get().needToDrawBackgroundColor( 3806 mNativeAwContents, AwContents.this)) { 3807 TraceEvent.instant("DrawBackgroundColor"); 3808 canvas.drawColor(getEffectiveBackgroundColor()); 3809 } 3810 if (did_draw && canvas.isHardwareAccelerated() 3811 && !ForceAuxiliaryBitmapRendering.sResult) { 3812 did_draw = mDrawFunctor.requestDraw(canvas); 3813 } 3814 if (did_draw) { 3815 int scrollXDiff = mContainerView.getScrollX() - scrollX; 3816 int scrollYDiff = mContainerView.getScrollY() - scrollY; 3817 canvas.translate(-scrollXDiff, -scrollYDiff); 3818 } else { 3819 TraceEvent.instant("NativeDrawFailed"); 3820 canvas.drawColor(getEffectiveBackgroundColor()); 3821 } 3822 3823 if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas, 3824 mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), 3825 mScrollOffsetManager.computeMaximumVerticalScrollOffset())) { 3826 postInvalidateOnAnimation(); 3827 } 3828 3829 if (mInvalidateRootViewOnNextDraw) { 3830 mContainerView.getRootView().invalidate(); 3831 mInvalidateRootViewOnNextDraw = false; 3832 } 3833 3834 // Tint everything one color, to make WebViews easier to spot. 3835 if (CommandLine.getInstance().hasSwitch(AwSwitches.HIGHLIGHT_ALL_WEBVIEWS)) { 3836 int semiTransparentYellow = Color.argb(80, 252, 252, 109); 3837 canvas.drawColor(semiTransparentYellow); 3838 } 3839 } 3840 3841 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)3842 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 3843 mLayoutSizer.onMeasure(widthMeasureSpec, heightMeasureSpec); 3844 } 3845 3846 @Override requestFocus()3847 public void requestFocus() { 3848 if (isDestroyed(NO_WARN)) return; 3849 if (!mContainerView.isInTouchMode() && mSettings.shouldFocusFirstNode()) { 3850 AwContentsJni.get().focusFirstNode(mNativeAwContents, AwContents.this); 3851 } 3852 } 3853 3854 @Override setLayerType(int layerType, Paint paint)3855 public void setLayerType(int layerType, Paint paint) { 3856 mLayerType = layerType; 3857 updateHardwareAcceleratedFeaturesToggle(); 3858 } 3859 updateHardwareAcceleratedFeaturesToggle()3860 private void updateHardwareAcceleratedFeaturesToggle() { 3861 mSettings.setEnableSupportedHardwareAcceleratedFeatures( 3862 mIsAttachedToWindow && mContainerView.isHardwareAccelerated() 3863 && (mLayerType == View.LAYER_TYPE_NONE 3864 || mLayerType == View.LAYER_TYPE_HARDWARE)); 3865 } 3866 3867 @Override onCreateInputConnection(EditorInfo outAttrs)3868 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 3869 return isDestroyed(NO_WARN) 3870 ? null 3871 : ImeAdapter.fromWebContents(mWebContents).onCreateInputConnection(outAttrs); 3872 } 3873 3874 @Override onDragEvent(DragEvent event)3875 public boolean onDragEvent(DragEvent event) { 3876 return isDestroyed(NO_WARN) 3877 ? false 3878 : mWebContents.getEventForwarder().onDragEvent(event, mContainerView); 3879 } 3880 3881 @Override onKeyUp(int keyCode, KeyEvent event)3882 public boolean onKeyUp(int keyCode, KeyEvent event) { 3883 return isDestroyed(NO_WARN) ? false 3884 : mWebContents.getEventForwarder().onKeyUp(keyCode, event); 3885 } 3886 3887 @Override dispatchKeyEvent(KeyEvent event)3888 public boolean dispatchKeyEvent(KeyEvent event) { 3889 if (isDestroyed(NO_WARN)) return false; 3890 if (isDpadEvent(event)) { 3891 mSettings.setSpatialNavigationEnabled(true); 3892 } 3893 3894 // Following check is dup'ed from |ContentUiEventHandler.dispatchKeyEvent| to avoid 3895 // embedder-specific customization, which is necessary only for WebView. 3896 if (GamepadList.dispatchKeyEvent(event)) return true; 3897 3898 // This check reflects Chrome's behavior and is a workaround for http://b/7697782. 3899 if (mContentsClient.hasWebViewClient() 3900 && mContentsClient.shouldOverrideKeyEvent(event)) { 3901 return mInternalAccessAdapter.super_dispatchKeyEvent(event); 3902 } 3903 return mWebContents.getEventForwarder().dispatchKeyEvent(event); 3904 } 3905 3906 @Override onTouchEvent(MotionEvent event)3907 public boolean onTouchEvent(MotionEvent event) { 3908 if (isDestroyed(NO_WARN)) return false; 3909 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 3910 mSettings.setSpatialNavigationEnabled(false); 3911 } 3912 3913 AwContentsJni.get().onInputEvent(mNativeAwContents, AwContents.this); 3914 3915 mScrollOffsetManager.setProcessingTouchEvent(true); 3916 boolean rv = mWebContents.getEventForwarder().onTouchEvent(event); 3917 mScrollOffsetManager.setProcessingTouchEvent(false); 3918 3919 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 3920 // Note this will trigger IPC back to browser even if nothing is 3921 // hit. 3922 float eventX = event.getX(); 3923 float eventY = event.getY(); 3924 float touchMajor = Math.max(event.getTouchMajor(), event.getTouchMinor()); 3925 if (!UseZoomForDSFPolicy.isUseZoomForDSFEnabled()) { 3926 float dipScale = getDeviceScaleFactor(); 3927 eventX /= dipScale; 3928 eventY /= dipScale; 3929 touchMajor /= dipScale; 3930 } 3931 AwContentsJni.get().requestNewHitTestDataAt( 3932 mNativeAwContents, AwContents.this, eventX, eventY, touchMajor); 3933 } 3934 3935 if (mOverScrollGlow != null) { 3936 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 3937 mOverScrollGlow.setShouldPull(true); 3938 } else if (event.getActionMasked() == MotionEvent.ACTION_UP 3939 || event.getActionMasked() == MotionEvent.ACTION_CANCEL) { 3940 mOverScrollGlow.setShouldPull(false); 3941 mOverScrollGlow.releaseAll(); 3942 } 3943 } 3944 3945 return rv; 3946 } 3947 3948 @Override onHoverEvent(MotionEvent event)3949 public boolean onHoverEvent(MotionEvent event) { 3950 return isDestroyed(NO_WARN) ? false 3951 : mWebContents.getEventForwarder().onHoverEvent(event); 3952 } 3953 3954 @Override onGenericMotionEvent(MotionEvent event)3955 public boolean onGenericMotionEvent(MotionEvent event) { 3956 return isDestroyed(NO_WARN) 3957 ? false 3958 : mWebContents.getEventForwarder().onGenericMotionEvent(event); 3959 } 3960 3961 @Override onConfigurationChanged(Configuration newConfig)3962 public void onConfigurationChanged(Configuration newConfig) { 3963 if (!isDestroyed(NO_WARN)) { 3964 mViewEventSink.onConfigurationChanged(newConfig); 3965 mInternalAccessAdapter.super_onConfigurationChanged(newConfig); 3966 } 3967 } 3968 3969 @Override onAttachedToWindow()3970 public void onAttachedToWindow() { 3971 if (isDestroyed(NO_WARN)) return; 3972 if (mIsAttachedToWindow) { 3973 Log.w(TAG, "onAttachedToWindow called when already attached. Ignoring"); 3974 return; 3975 } 3976 mIsAttachedToWindow = true; 3977 3978 mViewEventSink.onAttachedToWindow(); 3979 AwContentsJni.get().onAttachedToWindow(mNativeAwContents, AwContents.this, 3980 mContainerView.getWidth(), mContainerView.getHeight()); 3981 updateHardwareAcceleratedFeaturesToggle(); 3982 postUpdateWebContentsVisibility(); 3983 3984 updateDefaultLocale(); 3985 3986 if (mComponentCallbacks != null) return; 3987 mComponentCallbacks = new AwComponentCallbacks(); 3988 mContext.registerComponentCallbacks(mComponentCallbacks); 3989 } 3990 3991 @Override onDetachedFromWindow()3992 public void onDetachedFromWindow() { 3993 if (isDestroyed(NO_WARN)) return; 3994 if (!mIsAttachedToWindow) { 3995 Log.w(TAG, "onDetachedFromWindow called when already detached. Ignoring"); 3996 return; 3997 } 3998 mIsAttachedToWindow = false; 3999 hideAutofillPopup(); 4000 AwContentsJni.get().onDetachedFromWindow(mNativeAwContents, AwContents.this); 4001 4002 mViewEventSink.onDetachedFromWindow(); 4003 updateHardwareAcceleratedFeaturesToggle(); 4004 postUpdateWebContentsVisibility(); 4005 setFunctor(null); 4006 4007 if (mComponentCallbacks != null) { 4008 mContext.unregisterComponentCallbacks(mComponentCallbacks); 4009 mComponentCallbacks = null; 4010 } 4011 4012 mScrollAccessibilityHelper.removePostedCallbacks(); 4013 mZoomControls.dismissZoomPicker(); 4014 } 4015 4016 @Override onWindowFocusChanged(boolean hasWindowFocus)4017 public void onWindowFocusChanged(boolean hasWindowFocus) { 4018 if (isDestroyed(NO_WARN)) return; 4019 mWindowFocused = hasWindowFocus; 4020 mViewEventSink.onWindowFocusChanged(hasWindowFocus); 4021 Clipboard.getInstance().onWindowFocusChanged(hasWindowFocus); 4022 } 4023 4024 @Override onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)4025 public void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 4026 if (isDestroyed(NO_WARN)) return; 4027 mContainerViewFocused = focused; 4028 mViewEventSink.onViewFocusChanged(focused); 4029 } 4030 4031 @Override onSizeChanged(int w, int h, int ow, int oh)4032 public void onSizeChanged(int w, int h, int ow, int oh) { 4033 if (isDestroyed(NO_WARN)) return; 4034 mScrollOffsetManager.setContainerViewSize(w, h); 4035 // The AwLayoutSizer needs to go first so that if we're in 4036 // fixedLayoutSize mode the update 4037 // to enter fixedLayoutSize mode is sent before the first resize 4038 // update. 4039 mLayoutSizer.onSizeChanged(w, h, ow, oh); 4040 AwContentsJni.get().onSizeChanged(mNativeAwContents, AwContents.this, w, h, ow, oh); 4041 } 4042 4043 @Override onVisibilityChanged(View changedView, int visibility)4044 public void onVisibilityChanged(View changedView, int visibility) { 4045 boolean viewVisible = mContainerView.getVisibility() == View.VISIBLE; 4046 if (mIsViewVisible == viewVisible) return; 4047 setViewVisibilityInternal(viewVisible); 4048 } 4049 4050 @Override onWindowVisibilityChanged(int visibility)4051 public void onWindowVisibilityChanged(int visibility) { 4052 boolean windowVisible = visibility == View.VISIBLE; 4053 if (mIsWindowVisible == windowVisible) return; 4054 setWindowVisibilityInternal(windowVisible); 4055 } 4056 4057 @Override onContainerViewScrollChanged(int l, int t, int oldl, int oldt)4058 public void onContainerViewScrollChanged(int l, int t, int oldl, int oldt) { 4059 // A side-effect of View.onScrollChanged is that the scroll accessibility event being 4060 // sent by the base class implementation. This is completely hidden from the base 4061 // classes and cannot be prevented, which is why we need the code below. 4062 mScrollAccessibilityHelper.removePostedViewScrolledAccessibilityEventCallback(); 4063 mScrollOffsetManager.onContainerViewScrollChanged(l, t); 4064 } 4065 4066 @Override onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)4067 public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX, 4068 boolean clampedY) { 4069 int oldX = mContainerView.getScrollX(); 4070 int oldY = mContainerView.getScrollY(); 4071 4072 mScrollOffsetManager.onContainerViewOverScrolled(scrollX, scrollY, clampedX, clampedY); 4073 4074 if (mOverScrollGlow != null) { 4075 mOverScrollGlow.pullGlow(mContainerView.getScrollX(), mContainerView.getScrollY(), 4076 oldX, oldY, 4077 mScrollOffsetManager.computeMaximumHorizontalScrollOffset(), 4078 mScrollOffsetManager.computeMaximumVerticalScrollOffset()); 4079 } 4080 } 4081 4082 @Override computeHorizontalScrollRange()4083 public int computeHorizontalScrollRange() { 4084 return mScrollOffsetManager.computeHorizontalScrollRange(); 4085 } 4086 4087 @Override computeHorizontalScrollOffset()4088 public int computeHorizontalScrollOffset() { 4089 return mScrollOffsetManager.computeHorizontalScrollOffset(); 4090 } 4091 4092 @Override computeVerticalScrollRange()4093 public int computeVerticalScrollRange() { 4094 return mScrollOffsetManager.computeVerticalScrollRange(); 4095 } 4096 4097 @Override computeVerticalScrollOffset()4098 public int computeVerticalScrollOffset() { 4099 return mScrollOffsetManager.computeVerticalScrollOffset(); 4100 } 4101 4102 @Override computeVerticalScrollExtent()4103 public int computeVerticalScrollExtent() { 4104 return mScrollOffsetManager.computeVerticalScrollExtent(); 4105 } 4106 4107 @Override computeScroll()4108 public void computeScroll() { 4109 if (isDestroyed(NO_WARN)) return; 4110 AwContentsJni.get().onComputeScroll(mNativeAwContents, AwContents.this, 4111 AnimationUtils.currentAnimationTimeMillis()); 4112 } 4113 4114 @Override onCheckIsTextEditor()4115 public boolean onCheckIsTextEditor() { 4116 if (isDestroyed(NO_WARN)) return false; 4117 ImeAdapter imeAdapter = ImeAdapter.fromWebContents(mWebContents); 4118 return imeAdapter != null ? imeAdapter.onCheckIsTextEditor() : false; 4119 } 4120 4121 @Override getAccessibilityNodeProvider()4122 public AccessibilityNodeProvider getAccessibilityNodeProvider() { 4123 if (isDestroyed(NO_WARN)) return null; 4124 WebContentsAccessibility wcax = getWebContentsAccessibility(); 4125 return wcax != null ? wcax.getAccessibilityNodeProvider() : null; 4126 } 4127 4128 @Override performAccessibilityAction(final int action, final Bundle arguments)4129 public boolean performAccessibilityAction(final int action, final Bundle arguments) { 4130 if (isDestroyed(NO_WARN)) return false; 4131 WebContentsAccessibility wcax = getWebContentsAccessibility(); 4132 return wcax != null ? wcax.performAction(action, arguments) : false; 4133 } 4134 } 4135 4136 // Return true if the GeolocationPermissionAPI should be used. 4137 @CalledByNative useLegacyGeolocationPermissionAPI()4138 private boolean useLegacyGeolocationPermissionAPI() { 4139 // Always return true since we are not ready to swap the geolocation yet. 4140 // TODO: If we decide not to migrate the geolocation, there are some unreachable 4141 // code need to remove. http://crbug.com/396184. 4142 return true; 4143 } 4144 4145 @NativeMethods 4146 interface Natives { init(long browserContextPointer)4147 long init(long browserContextPointer); 4148 destroy(long nativeAwContents)4149 void destroy(long nativeAwContents); hasRequiredHardwareExtensions()4150 boolean hasRequiredHardwareExtensions(); setAwDrawSWFunctionTable(long functionTablePointer)4151 void setAwDrawSWFunctionTable(long functionTablePointer); setAwDrawGLFunctionTable(long functionTablePointer)4152 void setAwDrawGLFunctionTable(long functionTablePointer); getNativeInstanceCount()4153 int getNativeInstanceCount(); setShouldDownloadFavicons()4154 void setShouldDownloadFavicons(); updateDefaultLocale(String locale, String localeList)4155 void updateDefaultLocale(String locale, String localeList); getSafeBrowsingLocaleForTesting()4156 String getSafeBrowsingLocaleForTesting(); setJavaPeers(long nativeAwContents, AwContents caller, AwContents awContents, AwWebContentsDelegate webViewWebContentsDelegate, AwContentsClientBridge contentsClientBridge, AwContentsIoThreadClient ioThreadClient, InterceptNavigationDelegate navigationInterceptionDelegate, AutofillProvider autofillProvider)4157 void setJavaPeers(long nativeAwContents, AwContents caller, AwContents awContents, 4158 AwWebContentsDelegate webViewWebContentsDelegate, 4159 AwContentsClientBridge contentsClientBridge, 4160 AwContentsIoThreadClient ioThreadClient, 4161 InterceptNavigationDelegate navigationInterceptionDelegate, 4162 AutofillProvider autofillProvider); getWebContents(long nativeAwContents, AwContents caller)4163 WebContents getWebContents(long nativeAwContents, AwContents caller); getBrowserContext(long nativeAwContents, AwContents caller)4164 AwBrowserContext getBrowserContext(long nativeAwContents, AwContents caller); setCompositorFrameConsumer( long nativeAwContents, AwContents caller, long nativeCompositorFrameConsumer)4165 void setCompositorFrameConsumer( 4166 long nativeAwContents, AwContents caller, long nativeCompositorFrameConsumer); documentHasImages(long nativeAwContents, AwContents caller, Message message)4167 void documentHasImages(long nativeAwContents, AwContents caller, Message message); generateMHTML( long nativeAwContents, AwContents caller, String path, Callback<String> callback)4168 void generateMHTML( 4169 long nativeAwContents, AwContents caller, String path, Callback<String> callback); addVisitedLinks(long nativeAwContents, AwContents caller, String[] visitedLinks)4170 void addVisitedLinks(long nativeAwContents, AwContents caller, String[] visitedLinks); zoomBy(long nativeAwContents, AwContents caller, float delta)4171 void zoomBy(long nativeAwContents, AwContents caller, float delta); onComputeScroll( long nativeAwContents, AwContents caller, long currentAnimationTimeMillis)4172 void onComputeScroll( 4173 long nativeAwContents, AwContents caller, long currentAnimationTimeMillis); onDraw(long nativeAwContents, AwContents caller, Canvas canvas, boolean isHardwareAccelerated, int scrollX, int scrollY, int visibleLeft, int visibleTop, int visibleRight, int visibleBottom, boolean forceAuxiliaryBitmapRendering)4174 boolean onDraw(long nativeAwContents, AwContents caller, Canvas canvas, 4175 boolean isHardwareAccelerated, int scrollX, int scrollY, int visibleLeft, 4176 int visibleTop, int visibleRight, int visibleBottom, 4177 boolean forceAuxiliaryBitmapRendering); needToDrawBackgroundColor(long nativeAwContents, AwContents caller)4178 boolean needToDrawBackgroundColor(long nativeAwContents, AwContents caller); findAllAsync(long nativeAwContents, AwContents caller, String searchString)4179 void findAllAsync(long nativeAwContents, AwContents caller, String searchString); findNext(long nativeAwContents, AwContents caller, boolean forward)4180 void findNext(long nativeAwContents, AwContents caller, boolean forward); clearMatches(long nativeAwContents, AwContents caller)4181 void clearMatches(long nativeAwContents, AwContents caller); clearCache(long nativeAwContents, AwContents caller, boolean includeDiskFiles)4182 void clearCache(long nativeAwContents, AwContents caller, boolean includeDiskFiles); getCertificate(long nativeAwContents, AwContents caller)4183 byte[] getCertificate(long nativeAwContents, AwContents caller); 4184 // Coordinates are in physical pixels when --use-zoom-for-dsf is enabled. 4185 // Otherwise, coordinates are in desity independent pixels. requestNewHitTestDataAt( long nativeAwContents, AwContents caller, float x, float y, float touchMajor)4186 void requestNewHitTestDataAt( 4187 long nativeAwContents, AwContents caller, float x, float y, float touchMajor); 4188 updateLastHitTestData(long nativeAwContents, AwContents caller)4189 void updateLastHitTestData(long nativeAwContents, AwContents caller); onSizeChanged(long nativeAwContents, AwContents caller, int w, int h, int ow, int oh)4190 void onSizeChanged(long nativeAwContents, AwContents caller, int w, int h, int ow, int oh); scrollTo(long nativeAwContents, AwContents caller, int x, int y)4191 void scrollTo(long nativeAwContents, AwContents caller, int x, int y); restoreScrollAfterTransition(long nativeAwContents, AwContents caller, int x, int y)4192 void restoreScrollAfterTransition(long nativeAwContents, AwContents caller, int x, int y); smoothScroll(long nativeAwContents, AwContents caller, int targetX, int targetY, long durationMs)4193 void smoothScroll(long nativeAwContents, AwContents caller, int targetX, int targetY, 4194 long durationMs); setViewVisibility(long nativeAwContents, AwContents caller, boolean visible)4195 void setViewVisibility(long nativeAwContents, AwContents caller, boolean visible); setWindowVisibility(long nativeAwContents, AwContents caller, boolean visible)4196 void setWindowVisibility(long nativeAwContents, AwContents caller, boolean visible); setIsPaused(long nativeAwContents, AwContents caller, boolean paused)4197 void setIsPaused(long nativeAwContents, AwContents caller, boolean paused); onAttachedToWindow(long nativeAwContents, AwContents caller, int w, int h)4198 void onAttachedToWindow(long nativeAwContents, AwContents caller, int w, int h); onDetachedFromWindow(long nativeAwContents, AwContents caller)4199 void onDetachedFromWindow(long nativeAwContents, AwContents caller); isVisible(long nativeAwContents, AwContents caller)4200 boolean isVisible(long nativeAwContents, AwContents caller); isDisplayingInterstitialForTesting(long nativeAwContents, AwContents caller)4201 boolean isDisplayingInterstitialForTesting(long nativeAwContents, AwContents caller); setDipScale(long nativeAwContents, AwContents caller, float dipScale)4202 void setDipScale(long nativeAwContents, AwContents caller, float dipScale); 4203 isDisplayingOpenWebContent(long nativeAwContents, AwContents caller)4204 boolean isDisplayingOpenWebContent(long nativeAwContents, AwContents caller); updateOpenWebScreenArea(int pixels, int percentage)4205 void updateOpenWebScreenArea(int pixels, int percentage); 4206 onInputEvent(long nativeAwContents, AwContents caller)4207 void onInputEvent(long nativeAwContents, AwContents caller); 4208 // Returns null if save state fails. getOpaqueState(long nativeAwContents, AwContents caller)4209 byte[] getOpaqueState(long nativeAwContents, AwContents caller); 4210 4211 // Returns false if restore state fails. restoreFromOpaqueState(long nativeAwContents, AwContents caller, byte[] state)4212 boolean restoreFromOpaqueState(long nativeAwContents, AwContents caller, byte[] state); 4213 releasePopupAwContents(long nativeAwContents, AwContents caller)4214 long releasePopupAwContents(long nativeAwContents, AwContents caller); focusFirstNode(long nativeAwContents, AwContents caller)4215 void focusFirstNode(long nativeAwContents, AwContents caller); setBackgroundColor(long nativeAwContents, AwContents caller, int color)4216 void setBackgroundColor(long nativeAwContents, AwContents caller, int color); capturePicture(long nativeAwContents, AwContents caller, int width, int height)4217 long capturePicture(long nativeAwContents, AwContents caller, int width, int height); enableOnNewPicture(long nativeAwContents, AwContents caller, boolean enabled)4218 void enableOnNewPicture(long nativeAwContents, AwContents caller, boolean enabled); insertVisualStateCallback(long nativeAwContents, AwContents caller, long requestId, VisualStateCallback callback)4219 void insertVisualStateCallback(long nativeAwContents, AwContents caller, long requestId, 4220 VisualStateCallback callback); clearView(long nativeAwContents, AwContents caller)4221 void clearView(long nativeAwContents, AwContents caller); setExtraHeadersForUrl( long nativeAwContents, AwContents caller, String url, String extraHeaders)4222 void setExtraHeadersForUrl( 4223 long nativeAwContents, AwContents caller, String url, String extraHeaders); invokeGeolocationCallback( long nativeAwContents, AwContents caller, boolean value, String requestingFrame)4224 void invokeGeolocationCallback( 4225 long nativeAwContents, AwContents caller, boolean value, String requestingFrame); getEffectivePriority(long nativeAwContents, AwContents caller)4226 int getEffectivePriority(long nativeAwContents, AwContents caller); setJsOnlineProperty(long nativeAwContents, AwContents caller, boolean networkUp)4227 void setJsOnlineProperty(long nativeAwContents, AwContents caller, boolean networkUp); trimMemory(long nativeAwContents, AwContents caller, int level, boolean visible)4228 void trimMemory(long nativeAwContents, AwContents caller, int level, boolean visible); createPdfExporter( long nativeAwContents, AwContents caller, AwPdfExporter awPdfExporter)4229 void createPdfExporter( 4230 long nativeAwContents, AwContents caller, AwPdfExporter awPdfExporter); preauthorizePermission( long nativeAwContents, AwContents caller, String origin, long resources)4231 void preauthorizePermission( 4232 long nativeAwContents, AwContents caller, String origin, long resources); grantFileSchemeAccesstoChildProcess(long nativeAwContents, AwContents caller)4233 void grantFileSchemeAccesstoChildProcess(long nativeAwContents, AwContents caller); resumeLoadingCreatedPopupWebContents(long nativeAwContents, AwContents caller)4234 void resumeLoadingCreatedPopupWebContents(long nativeAwContents, AwContents caller); getRenderProcess(long nativeAwContents, AwContents caller)4235 AwRenderProcess getRenderProcess(long nativeAwContents, AwContents caller); addDocumentStartJavaScript(long nativeAwContents, AwContents caller, String script, String[] allowedOriginRules)4236 int addDocumentStartJavaScript(long nativeAwContents, AwContents caller, String script, 4237 String[] allowedOriginRules); removeDocumentStartJavaScript(long nativeAwContents, AwContents caller, int scriptId)4238 void removeDocumentStartJavaScript(long nativeAwContents, AwContents caller, int scriptId); addWebMessageListener(long nativeAwContents, AwContents caller, WebMessageListenerHolder listener, String jsObjectName, String[] allowedOrigins)4239 String addWebMessageListener(long nativeAwContents, AwContents caller, 4240 WebMessageListenerHolder listener, String jsObjectName, String[] allowedOrigins); removeWebMessageListener( long nativeAwContents, AwContents caller, String jsObjectName)4241 void removeWebMessageListener( 4242 long nativeAwContents, AwContents caller, String jsObjectName); getJsObjectsInfo( long nativeAwContents, AwContents caller, Class clazz)4243 WebMessageListenerInfo[] getJsObjectsInfo( 4244 long nativeAwContents, AwContents caller, Class clazz); 4245 } 4246 } 4247