-
Notifications
You must be signed in to change notification settings - Fork 6.6k
/
AwContents.java
4500 lines (3915 loc) · 183 KB
/
AwContents.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.net.http.SslCertificate;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Pair;
import android.util.SparseArray;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStructure;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.textclassifier.TextClassifier;
import android.webkit.JavascriptInterface;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import org.chromium.android_webview.autofill.AndroidAutofillSafeModeAction;
import org.chromium.android_webview.common.AwFeatures;
import org.chromium.android_webview.common.AwSwitches;
import org.chromium.android_webview.gfx.AwDrawFnImpl;
import org.chromium.android_webview.gfx.AwFunctor;
import org.chromium.android_webview.gfx.AwGLFunctor;
import org.chromium.android_webview.gfx.AwPicture;
import org.chromium.android_webview.gfx.RectUtils;
import org.chromium.android_webview.metrics.AwOriginVisitLogger;
import org.chromium.android_webview.metrics.AwSiteVisitLogger;
import org.chromium.android_webview.permission.AwGeolocationCallback;
import org.chromium.android_webview.permission.AwPermissionRequest;
import org.chromium.android_webview.renderer_priority.RendererPriority;
import org.chromium.base.Callback;
import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils;
import org.chromium.base.LocaleUtils;
import org.chromium.base.Log;
import org.chromium.base.ObserverList;
import org.chromium.base.ThreadUtils;
import org.chromium.base.TraceEvent;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.CalledByNativeUnchecked;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.ScopedSysTraceEvent;
import org.chromium.base.task.AsyncTask;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.components.autofill.AutofillActionModeCallback;
import org.chromium.components.autofill.AutofillProvider;
import org.chromium.components.content_capture.OnscreenContentProvider;
import org.chromium.components.embedder_support.util.WebResourceResponseInfo;
import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
import org.chromium.components.stylus_handwriting.StylusWritingController;
import org.chromium.components.url_formatter.UrlFormatter;
import org.chromium.content_public.browser.ChildProcessImportance;
import org.chromium.content_public.browser.ContentViewStatics;
import org.chromium.content_public.browser.GestureListenerManager;
import org.chromium.content_public.browser.GestureStateListener;
import org.chromium.content_public.browser.ImeAdapter;
import org.chromium.content_public.browser.ImeEventObserver;
import org.chromium.content_public.browser.JavaScriptCallback;
import org.chromium.content_public.browser.JavascriptInjector;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.MessagePayload;
import org.chromium.content_public.browser.MessagePort;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.NavigationHistory;
import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.SelectionClient;
import org.chromium.content_public.browser.SelectionPopupController;
import org.chromium.content_public.browser.SmartClipProvider;
import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.content_public.browser.ViewEventSink;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsAccessibility;
import org.chromium.content_public.browser.WebContentsInternals;
import org.chromium.content_public.browser.navigation_controller.LoadURLType;
import org.chromium.content_public.browser.navigation_controller.UserAgentOverrideOption;
import org.chromium.content_public.common.ContentUrlConstants;
import org.chromium.content_public.common.Referrer;
import org.chromium.device.gamepad.GamepadList;
import org.chromium.net.NetworkChangeNotifier;
import org.chromium.network.mojom.ReferrerPolicy;
import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.Clipboard;
import org.chromium.ui.base.IntentRequestTracker;
import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.ViewAndroidDelegate;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.display.DisplayAndroid.DisplayAndroidObserver;
import org.chromium.url.GURL;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Exposes the native AwContents class, and together these classes wrap the WebContents
* and Browser components that are required to implement Android WebView API. This is the
* primary entry point for the WebViewProvider implementation; it holds a 1:1 object
* relationship with application WebView instances.
* (We define this class independent of the hidden WebViewProvider interfaces, to allow
* continuous build & test in the open source SDK-based tree).
*/
@JNINamespace("android_webview")
public class AwContents implements SmartClipProvider {
private static final String TAG = "AwContents";
private static final boolean TRACE = false;
private static final int NO_WARN = 0;
private static final int WARN = 1;
private static final String PRODUCT_VERSION = AwContentsStatics.getProductVersion();
private static final String WEB_ARCHIVE_EXTENSION = ".mht";
// The request code should be unique per WebView/AwContents object.
private static final int PROCESS_TEXT_REQUEST_CODE = 100;
// Used to avoid enabling zooming in / out if resulting zooming will
// produce little visible difference.
private static final float ZOOM_CONTROLS_EPSILON = 0.007f;
private static final double MIN_SCREEN_HEIGHT_PERCENTAGE_FOR_INTERSTITIAL = 0.7;
private static final String SAMSUNG_WORKAROUND_BASE_URL = "email://";
private static final int SAMSUNG_WORKAROUND_DELAY = 200;
@VisibleForTesting
public static final String LOAD_URL_SCHEME_HISTOGRAM_NAME = "Android.WebView.LoadUrl.UrlScheme";
// Permit any number of slashes, since chromium seems to canonicalize bad values.
private static final Pattern sFileAndroidAssetPattern =
Pattern.compile("^file:/*android_(asset|res).*");
// Matches a data URL that (may) have a valid fragment selector, pulling the fragment selector
// out into a group. Such a URL must contain a single '#' character and everything after that
// must be a valid DOM id.
// DOM id grammar: https://www.w3.org/TR/1999/REC-html401-19991224/types.html#type-name
private static final Pattern sDataURLWithSelectorPattern =
Pattern.compile("^[^#]*(#[A-Za-z][A-Za-z0-9\\-_:.]*)$");
private static class ForceAuxiliaryBitmapRendering {
private static final boolean sResult = lazyCheck();
private static boolean lazyCheck() {
return !AwContentsJni.get().hasRequiredHardwareExtensions();
}
}
// Used to record the UMA histogram Android.WebView.LoadDataWithBaseUrl.UrlScheme. Since these
// values are persisted to logs, they should never be renumbered or reused.
@VisibleForTesting
@IntDef({UrlScheme.EMPTY, UrlScheme.UNKNOWN_SCHEME, UrlScheme.HTTP_SCHEME,
UrlScheme.HTTPS_SCHEME, UrlScheme.FILE_SCHEME, UrlScheme.FTP_SCHEME,
UrlScheme.DATA_SCHEME, UrlScheme.JAVASCRIPT_SCHEME, UrlScheme.ABOUT_SCHEME,
UrlScheme.CHROME_SCHEME, UrlScheme.BLOB_SCHEME, UrlScheme.CONTENT_SCHEME,
UrlScheme.INTENT_SCHEME, UrlScheme.FILE_ANDROID_ASSET_SCHEME})
public @interface UrlScheme {
int EMPTY = 0;
int UNKNOWN_SCHEME = 1;
int HTTP_SCHEME = 2;
int HTTPS_SCHEME = 3;
int FILE_SCHEME = 4;
int FTP_SCHEME = 5;
int DATA_SCHEME = 6;
int JAVASCRIPT_SCHEME = 7;
int ABOUT_SCHEME = 8;
int CHROME_SCHEME = 9;
int BLOB_SCHEME = 10;
int CONTENT_SCHEME = 11;
int INTENT_SCHEME = 12;
int FILE_ANDROID_ASSET_SCHEME = 13; // Covers android_asset and android_res URLs
int COUNT = 14;
}
/**
* WebKit hit test related data structure. These are used to implement
* getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView.
* All values should be updated together. The native counterpart is
* AwHitTestData.
*/
public static class HitTestData {
// Used in getHitTestResult.
public int hitTestResultType;
public String hitTestResultExtraData;
// Used in requestFocusNodeHref (all three) and requestImageRef (only imgSrc).
public String href;
public String anchorText;
public String imgSrc;
}
/**
* Interface that consumers of {@link AwContents} must implement to allow the proper
* dispatching of view methods through the containing view.
*/
public interface InternalAccessDelegate extends ViewEventSink.InternalAccessDelegate {
/**
* @see View#overScrollBy(int, int, int, int, int, int, int, int, boolean);
*/
void overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent);
/**
* @see View#scrollTo(int, int)
*/
void super_scrollTo(int scrollX, int scrollY);
/**
* @see View#setMeasuredDimension(int, int)
*/
void setMeasuredDimension(int measuredWidth, int measuredHeight);
/**
* @see View#getScrollBarStyle()
*/
int super_getScrollBarStyle();
/**
* @see View#startActivityForResult(Intent, int)
*/
void super_startActivityForResult(Intent intent, int requestCode);
/**
* @see View#onConfigurationChanged(Configuration)
*/
void super_onConfigurationChanged(Configuration newConfig);
}
/**
* Factory interface used for constructing functors that the Android framework uses for
* calling back into Chromium code to render the the contents of a Chromium frame into
* an Android view.
*/
public interface NativeDrawFunctorFactory {
/**
* Create a GL functor associated with native context |context|.
*/
NativeDrawGLFunctor createGLFunctor(long context);
/**
* Used for draw_fn functor. Only one of these methods need to return non-null.
* Prefer this over createGLFunctor.
*/
AwDrawFnImpl.DrawFnAccess getDrawFnAccess();
}
/**
* Interface that consumers of {@link AwContents} must implement to support
* native GL rendering.
*/
public interface NativeDrawGLFunctor {
/**
* Requests a callback on the native DrawGL method (see getAwDrawGLFunction).
*
* If called from within onDraw, |canvas| should be non-null and must be hardware
* accelerated. |releasedCallback| should be null if |canvas| is null, or if
* supportsDrawGLFunctorReleasedCallback returns false.
*
* @return false indicates the GL draw request was not accepted, and the caller
* should fallback to the SW path.
*/
boolean requestDrawGL(Canvas canvas, Runnable releasedCallback);
/**
* Requests a callback on the native DrawGL method (see getAwDrawGLFunction).
*
* |containerView| must be hardware accelerated. If |waitForCompletion| is true, this method
* will not return until functor has returned.
*/
boolean requestInvokeGL(View containerView, boolean waitForCompletion);
/**
* Test whether the Android framework supports notifying when a functor is free
* to be destroyed via the callback mechanism provided to the functor factory.
*
* @return true if destruction needs to wait on a framework callback, or false
* if it can occur immediately.
*/
boolean supportsDrawGLFunctorReleasedCallback();
/**
* Detaches the GLFunctor from the view tree.
*/
void detach(View containerView);
/**
* Destroy this functor instance and any native objects associated with it. No method is
* called after destroy.
*/
void destroy();
}
/**
* Class to facilitate dependency injection. Subclasses by test code to provide mock versions of
* certain AwContents dependencies.
*/
public static class DependencyFactory {
public AwLayoutSizer createLayoutSizer() {
return new AwLayoutSizer();
}
public AwScrollOffsetManager createScrollOffsetManager(
AwScrollOffsetManager.Delegate delegate) {
return new AwScrollOffsetManager(delegate);
}
}
/**
* Visual state callback, see {@link #insertVisualStateCallback} for details.
*
*/
@VisibleForTesting
public abstract static class VisualStateCallback {
/**
* @param requestId the id passed to {@link AwContents#insertVisualStateCallback}
* which can be used to match requests with the corresponding callbacks.
*/
public abstract void onComplete(long requestId);
}
private long mNativeAwContents;
private AwBrowserContext mBrowserContext;
private ViewGroup mContainerView;
private AwFunctor mDrawFunctor;
private final Context mContext;
private final int mAppTargetSdkVersion;
private AwViewAndroidDelegate mViewAndroidDelegate;
private WindowAndroidWrapper mWindowAndroid;
private WebContents mWebContents;
private ViewEventSink mViewEventSink;
private WebContentsInternalsHolder mWebContentsInternalsHolder;
private NavigationController mNavigationController;
private final AwContentsClient mContentsClient;
private AwWebContentsObserver mWebContentsObserver;
private final AwContentsClientBridge mContentsClientBridge;
private final AwWebContentsDelegateAdapter mWebContentsDelegate;
private final AwContentsBackgroundThreadClient mBackgroundThreadClient;
private final AwContentsIoThreadClient mIoThreadClient;
private final InterceptNavigationDelegateImpl mInterceptNavigationDelegate;
private InternalAccessDelegate mInternalAccessAdapter;
private final NativeDrawFunctorFactory mNativeDrawFunctorFactory;
private final AwLayoutSizer mLayoutSizer;
private final AwZoomControls mZoomControls;
private final AwScrollOffsetManager mScrollOffsetManager;
private OverScrollGlow mOverScrollGlow;
private final DisplayAndroidObserver mDisplayObserver;
// This can be accessed on any thread after construction. See AwContentsIoThreadClient.
private final AwSettings mSettings;
private final ScrollAccessibilityHelper mScrollAccessibilityHelper;
private final ObserverList<PopupTouchHandleDrawable> mTouchHandleDrawables =
new ObserverList<>();
private boolean mIsPaused;
private boolean mIsViewVisible;
private boolean mIsWindowVisible;
private boolean mIsAttachedToWindow;
// Visiblity state of |mWebContents|.
private boolean mIsContentVisible;
private boolean mIsUpdateVisibilityTaskPending;
private Runnable mUpdateVisibilityRunnable;
private @RendererPriority int mRendererPriority;
private boolean mRendererPriorityWaivedWhenNotVisible;
private Bitmap mFavicon;
private boolean mHasRequestedVisitedHistoryFromClient;
// Whether this WebView is a popup.
private boolean mIsPopupWindow;
// The base background color, i.e. not accounting for any CSS body from the current page.
private int mBaseBackgroundColor = Color.WHITE;
// Did background set by developer, now used for dark mode.
private boolean mDidInitBackground;
// Must call AwContentsJni.get().updateLastHitTestData first to update this before use.
private final HitTestData mPossiblyStaleHitTestData = new HitTestData();
private final DefaultVideoPosterRequestHandler mDefaultVideoPosterRequestHandler;
// Bound method for suppling Picture instances to the AwContentsClient. Will be null if the
// picture listener API has not yet been enabled, or if it is using invalidation-only mode.
private Callable<Picture> mPictureListenerContentProvider;
private boolean mContainerViewFocused;
private boolean mWindowFocused;
// These come from the compositor and are updated synchronously (in contrast to the values in
// RenderCoordinates, which are updated at end of every frame).
private float mPageScaleFactor = 1.0f;
private float mMinPageScaleFactor = 1.0f;
private float mMaxPageScaleFactor = 1.0f;
private float mContentWidthDip;
private float mContentHeightDip;
private AwAutofillClient mAwAutofillClient;
private AwPdfExporter mAwPdfExporter;
private AwViewMethods mAwViewMethods;
private final FullScreenTransitionsState mFullScreenTransitionsState;
// This is a workaround for some qualcomm devices discarding buffer on
// Activity restore.
private boolean mInvalidateRootViewOnNextDraw;
// The framework may temporarily detach our container view, for example during layout if
// we are a child of a ListView. This may cause many toggles of View focus, which we suppress
// when in this state.
private boolean mTemporarilyDetached;
// True when this AwContents has been destroyed.
// Do not use directly, call isDestroyed() instead.
private boolean mIsDestroyed;
private AutofillProvider mAutofillProvider;
private static String sCurrentLocales = "";
private Paint mPaintForNWorkaround;
// A holder of objects passed from WebContents and should be owned by AwContents that may
// have direct or indirect reference back to WebView. They are used internally by
// WebContents but all the references can create a new gc root that can keep WebView
// instances from being freed when they are detached from view tree, hence lead to
// memory leak. To avoid the issue, it is possible to use |WebContents.setInternalHolder|
// to move the holder of those internal objects to AwContents. Note that they are still
// used by WebContents, and AwContents doesn't have to know what's inside the holder.
private WebContentsInternals mWebContentsInternals;
private JavascriptInjector mJavascriptInjector;
private OnscreenContentProvider mOnscreenContentProvider;
private AwDisplayCutoutController mDisplayCutoutController;
private final AwDisplayModeController mDisplayModeController;
private final Rect mCachedSafeAreaRect = new Rect();
// The current AwWindowCoverageTracker, if any. This will be non-null when the AwContents is
// attached to the Window and size tracking is enabled. It will be null otherwise.
private AwWindowCoverageTracker mAwWindowCoverageTracker;
private AwDarkMode mAwDarkMode;
private AwWebContentsMetricsRecorder mAwWebContentsMetricsRecorder;
private StylusWritingController mStylusWritingController;
private static class WebContentsInternalsHolder implements WebContents.InternalsHolder {
private final WeakReference<AwContents> mAwContentsRef;
private WebContentsInternalsHolder(AwContents awContents) {
mAwContentsRef = new WeakReference<>(awContents);
}
@Override
public void set(WebContentsInternals internals) {
AwContents awContents = mAwContentsRef.get();
if (awContents == null) {
throw new IllegalStateException("AwContents should be available at this time");
}
awContents.mWebContentsInternals = internals;
}
@Override
public WebContentsInternals get() {
AwContents awContents = mAwContentsRef.get();
return awContents == null ? null : awContents.mWebContentsInternals;
}
public boolean weakRefCleared() {
return mAwContentsRef.get() == null;
}
}
private static final class AwContentsDestroyRunnable implements Runnable {
private final long mNativeAwContents;
// Hold onto a reference to the window (via its wrapper), so that it is not destroyed
// until we are done here.
private final WindowAndroidWrapper mWindowAndroid;
private AwContentsDestroyRunnable(
long nativeAwContents, WindowAndroidWrapper windowAndroid) {
mNativeAwContents = nativeAwContents;
mWindowAndroid = windowAndroid;
mWindowAndroid.incrementRefFromDestroyRunnable();
}
@Override
public void run() {
AwContentsJni.get().destroy(mNativeAwContents);
mWindowAndroid.decrementRefFromDestroyRunnable();
}
}
/**
* A class that stores the state needed to enter and exit fullscreen.
*/
private static class FullScreenTransitionsState {
private final ViewGroup mInitialContainerView;
private final InternalAccessDelegate mInitialInternalAccessAdapter;
private final AwViewMethods mInitialAwViewMethods;
private FullScreenView mFullScreenView;
/** Whether the initial container view was focused when we entered fullscreen */
private boolean mWasInitialContainerViewFocused;
private int mScrollX;
private int mScrollY;
private FullScreenTransitionsState(ViewGroup initialContainerView,
InternalAccessDelegate initialInternalAccessAdapter,
AwViewMethods initialAwViewMethods) {
mInitialContainerView = initialContainerView;
mInitialInternalAccessAdapter = initialInternalAccessAdapter;
mInitialAwViewMethods = initialAwViewMethods;
}
private void enterFullScreen(FullScreenView fullScreenView,
boolean wasInitialContainerViewFocused, int scrollX, int scrollY) {
mFullScreenView = fullScreenView;
mWasInitialContainerViewFocused = wasInitialContainerViewFocused;
mScrollX = scrollX;
mScrollY = scrollY;
}
private boolean wasInitialContainerViewFocused() {
return mWasInitialContainerViewFocused;
}
private int getScrollX() {
return mScrollX;
}
private int getScrollY() {
return mScrollY;
}
private void exitFullScreen() {
mFullScreenView = null;
}
private boolean isFullScreen() {
return mFullScreenView != null;
}
private ViewGroup getInitialContainerView() {
return mInitialContainerView;
}
private InternalAccessDelegate getInitialInternalAccessDelegate() {
return mInitialInternalAccessAdapter;
}
private AwViewMethods getInitialAwViewMethods() {
return mInitialAwViewMethods;
}
private FullScreenView getFullScreenView() {
return mFullScreenView;
}
}
// Reference to the active mNativeAwContents pointer while it is active use
// (ie before it is destroyed).
private CleanupReference mCleanupReference;
//--------------------------------------------------------------------------------------------
private class IoThreadClientImpl extends AwContentsIoThreadClient {
// All methods are called on the IO thread.
@Override
public int getCacheMode() {
return mSettings.getCacheMode();
}
@Override
public AwContentsBackgroundThreadClient getBackgroundThreadClient() {
return mBackgroundThreadClient;
}
@Override
public boolean shouldBlockContentUrls() {
return !mSettings.getAllowContentAccess();
}
@Override
public boolean shouldBlockFileUrls() {
return !mSettings.getAllowFileAccess();
}
@Override
public boolean shouldBlockNetworkLoads() {
return mSettings.getBlockNetworkLoads();
}
@Override
public boolean shouldAcceptThirdPartyCookies() {
return mSettings.getAcceptThirdPartyCookies();
}
@Override
public boolean getSafeBrowsingEnabled() {
return mSettings.getSafeBrowsingEnabled();
}
}
private class BackgroundThreadClientImpl extends AwContentsBackgroundThreadClient {
// All methods are called on the background thread.
@Override
public WebResourceResponseInfo shouldInterceptRequest(
AwContentsClient.AwWebResourceRequest request) {
String url = request.url;
WebResourceResponseInfo webResourceResponseInfo;
// Return the response directly if the url is default video poster url.
webResourceResponseInfo = mDefaultVideoPosterRequestHandler.shouldInterceptRequest(url);
if (webResourceResponseInfo != null) return webResourceResponseInfo;
webResourceResponseInfo = mContentsClient.shouldInterceptRequest(request);
if (webResourceResponseInfo == null) {
mContentsClient.getCallbackHelper().postOnLoadResource(url);
}
if (webResourceResponseInfo != null && webResourceResponseInfo.getData() == null) {
// In this case the intercepted URLRequest job will simulate an empty response
// which doesn't trigger the onReceivedError callback. For WebViewClassic
// compatibility we synthesize that callback. http://crbug.com/180950
mContentsClient.getCallbackHelper().postOnReceivedError(
request,
/* error description filled in by the glue layer */
new AwContentsClient.AwWebResourceError());
}
return webResourceResponseInfo;
}
@Override
public boolean shouldBlockRequest(String url) {
if (!AwFeatureList.isEnabled(AwFeatures.WEBVIEW_RESTRICT_THIRD_PARTY_CONTENT)) {
return false;
}
// TODO(1376958): Implement a URLLoaderThrottle to not block the IO thread before
// enabling the feature.
CountDownLatch countDownLatch = new CountDownLatch(1);
AtomicBoolean verified = new AtomicBoolean(false);
// Verifications are scheduled when WebView is initialized, so when this is called, the
// verification is likely finished here.
if (AwOriginVerificationScheduler.getInstance().getOriginVerifier().checkForSavedResult(
url)) {
return false;
}
AwThreadUtils.postToUiThreadLooper(() -> {
AwOriginVerificationScheduler.getInstance().verify(
url, mBrowserContext, (result) -> {
verified.set(result);
countDownLatch.countDown();
});
});
try {
countDownLatch.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// Returning the default value as no successful verification was performed.
}
return !verified.get();
}
}
//--------------------------------------------------------------------------------------------
// When the navigation is for a newly created WebView (i.e. a popup), intercept the navigation
// here for implementing shouldOverrideUrlLoading. This is to send the shouldOverrideUrlLoading
// callback to the correct WebViewClient that is associated with the WebView.
// Otherwise, use this delegate only to post onPageStarted messages.
//
// We are not using WebContentsObserver.didStartLoading because of stale URLs, out of order
// onPageStarted's and double onPageStarted's.
//
private class InterceptNavigationDelegateImpl extends InterceptNavigationDelegate {
@Override
public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle, GURL escapedUrl) {
// The shouldOverrideUrlLoading call might have resulted in posting messages to the
// UI thread. Using sendMessage here (instead of calling onPageStarted directly)
// will allow those to run in order.
if (!AwFeatureList.pageStartedOnCommitEnabled(navigationHandle.isRendererInitiated())) {
GURL url = navigationHandle.getBaseUrlForDataUrl().isEmpty()
? navigationHandle.getUrl()
: navigationHandle.getBaseUrlForDataUrl();
mContentsClient.getCallbackHelper().postOnPageStarted(url.getPossiblyInvalidSpec());
}
return false;
}
}
//--------------------------------------------------------------------------------------------
private class AwLayoutSizerDelegate implements AwLayoutSizer.Delegate {
@Override
public void requestLayout() {
ViewUtils.requestLayout(
mContainerView, "AwContents.AwLayoutSizerDelegate.requestLayout");
}
@Override
public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight);
}
@Override
public boolean isLayoutParamsHeightWrapContent() {
return mContainerView.getLayoutParams() != null
&& (mContainerView.getLayoutParams().height
== ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void setForceZeroLayoutHeight(boolean forceZeroHeight) {
getSettings().setForceZeroLayoutHeight(forceZeroHeight);
}
}
//--------------------------------------------------------------------------------------------
private class AwScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate {
@Override
public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY, boolean isTouchEvent) {
mInternalAccessAdapter.overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, 0, 0, isTouchEvent);
}
@Override
public void scrollContainerViewTo(int x, int y) {
try {
mInternalAccessAdapter.super_scrollTo(x, y);
} catch (Throwable e) {
AwThreadUtils.postToCurrentLooper(() -> {
Log.e(TAG, "The following exception was raised by scrollContainerViewTo:");
throw e;
});
}
}
@Override
public void scrollNativeTo(int x, int y) {
if (!isDestroyed(NO_WARN)) {
AwContentsJni.get().scrollTo(mNativeAwContents, x, y);
}
}
@Override
public void smoothScroll(int targetX, int targetY, long durationMs) {
if (!isDestroyed(NO_WARN)) {
AwContentsJni.get().smoothScroll(mNativeAwContents, targetX, targetY, durationMs);
}
}
@Override
public int getContainerViewScrollX() {
return mContainerView.getScrollX();
}
@Override
public int getContainerViewScrollY() {
return mContainerView.getScrollY();
}
@Override
public void invalidate() {
mContainerView.postInvalidateOnAnimation();
}
@Override
public void cancelFling() {
mWebContents.getEventForwarder().cancelFling(SystemClock.uptimeMillis());
}
}
//--------------------------------------------------------------------------------------------
private class AwGestureStateListener extends GestureStateListener {
@Override
public void onPinchStarted() {
// While it's possible to re-layout the view during a pinch gesture, the effect is very
// janky (especially that the page scale update notification comes from the renderer
// main thread, not from the impl thread, so it's usually out of sync with what's on
// screen). It's also quite expensive to do a re-layout, so we simply postpone
// re-layout for the duration of the gesture. This is compatible with what
// WebViewClassic does.
mLayoutSizer.freezeLayoutRequests();
}
@Override
public void onPinchEnded() {
mLayoutSizer.unfreezeLayoutRequests();
}
@Override
public void onScrollUpdateGestureConsumed(Point rootScrollOffset) {
mScrollAccessibilityHelper.postViewScrolledAccessibilityEventCallback();
mZoomControls.invokeZoomPicker();
}
@Override
public void onScrollStarted(int scrollOffsetY, int scrollExtentY, boolean isDirectionUp) {
mZoomControls.invokeZoomPicker();
}
@Override
public void onScaleLimitsChanged(float minPageScaleFactor, float maxPageScaleFactor) {
mZoomControls.updateZoomControls();
}
}
//--------------------------------------------------------------------------------------------
private class AwComponentCallbacks implements ComponentCallbacks2 {
@Override
public void onTrimMemory(final int level) {
boolean visibleRectEmpty = getGlobalVisibleRect().isEmpty();
final boolean visible = mIsViewVisible && mIsWindowVisible && !visibleRectEmpty;
ThreadUtils.runOnUiThreadBlocking(() -> {
if (isDestroyed(NO_WARN)) return;
if (level >= TRIM_MEMORY_MODERATE) {
if (mDrawFunctor != null) {
mDrawFunctor.trimMemory();
}
}
AwContentsJni.get().trimMemory(mNativeAwContents, level, visible);
});
}
@Override
public void onLowMemory() {}
@Override
public void onConfigurationChanged(Configuration configuration) {
updateDefaultLocale();
}
};
//--------------------------------------------------------------------------------------------
private class AwDisplayAndroidObserver implements DisplayAndroidObserver {
@Override
public void onRotationChanged(int rotation) {}
@Override
public void onDIPScaleChanged(float dipScale) {
if (TRACE) Log.i(TAG, "%s onDIPScaleChanged dipScale=%f", this, dipScale);
AwContentsJni.get().setDipScale(mNativeAwContents, dipScale);
mLayoutSizer.setDIPScale(dipScale);
mSettings.setDIPScale(dipScale);
}
};
/**
* Tracks and reports the percentage of coverage of AwContents on the root view.
*/
@VisibleForTesting
public static class AwWindowCoverageTracker {
private static final long RECALCULATION_DELAY_MS = 200;
@VisibleForTesting
public static final Map<View, AwWindowCoverageTracker> sWindowCoverageTrackers =
new HashMap<>();
private final View mRootView;
private List<AwContents> mAwContentsList = new ArrayList<>();
private long mRecalculationTime;
private boolean mPendingRecalculation;
private AwWindowCoverageTracker(View rootView) {
mRootView = rootView;
sWindowCoverageTrackers.put(rootView, this);
}
public static AwWindowCoverageTracker getOrCreateForRootView(
AwContents contents, View rootView) {
AwWindowCoverageTracker tracker = sWindowCoverageTrackers.get(rootView);
if (tracker == null) {
if (TRACE) {
Log.i(TAG, "%s creating WindowCoverageTracker for %s", contents, rootView);
}
tracker = new AwWindowCoverageTracker(rootView);
}
return tracker;
}
public void trackContents(AwContents contents) {
contents.mAwWindowCoverageTracker = this;
mAwContentsList.add(contents);
}
public void untrackContents(AwContents contents) {
contents.mAwWindowCoverageTracker = null;
mAwContentsList.remove(contents);
// If that was the last AwContents, remove ourselves from the static map.
if (!isTracking()) {
if (TRACE) Log.i(TAG, "%s removing " + this, contents);
sWindowCoverageTrackers.remove(mRootView);
}
}
private boolean isTracking() {
return mAwContentsList.size() > 0;
}
/**
* Notifies this object that a recalculation of the window coverage is necessary.
*
* This should be called every time any of the tracked AwContents changes its size,
* visibility, or scheme.
*
* Recalculation won't happen immediately, and will be rate limited.
*/
public void onInputsUpdated() {
long time = SystemClock.uptimeMillis();
if (mPendingRecalculation) return;
mPendingRecalculation = true;
if (time > mRecalculationTime + RECALCULATION_DELAY_MS) {
// Enough time has elapsed since the last recalculation, run it now.
mRecalculationTime = time;
} else {
// Not enough time has elapsed, run it once enough time has elapsed.
mRecalculationTime += RECALCULATION_DELAY_MS;
}
PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, () -> {
recalculate();
mPendingRecalculation = false;
}, mRecalculationTime - time);
}
private static int[] toIntArray(List<Integer> list) {
int[] array = new int[list.size()];
for (int i = 0; i < list.size(); i++) {
array[i] = list.get(i);
}
return array;
}
private void recalculate() {
if (TRACE) Log.i(TAG, "%s recalculate", this);
List<Rect> contentRects = new ArrayList<>();