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