| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package com.android.webview.chromium; |
| |
| import android.annotation.TargetApi; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.SharedPreferences; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageInfo; |
| import android.net.Uri; |
| import android.os.Build; |
| import android.os.SystemClock; |
| import android.provider.Settings; |
| import android.webkit.CookieManager; |
| import android.webkit.GeolocationPermissions; |
| import android.webkit.PacProcessor; |
| import android.webkit.ServiceWorkerController; |
| import android.webkit.TokenBindingService; |
| import android.webkit.TracingController; |
| import android.webkit.ValueCallback; |
| import android.webkit.WebStorage; |
| import android.webkit.WebView; |
| import android.webkit.WebViewDatabase; |
| import android.webkit.WebViewFactory; |
| import android.webkit.WebViewFactoryProvider; |
| import android.webkit.WebViewProvider; |
| |
| import androidx.annotation.IntDef; |
| |
| import com.android.webview.chromium.WebViewDelegateFactory.WebViewDelegate; |
| |
| import org.chromium.android_webview.ApkType; |
| import org.chromium.android_webview.AwBrowserContext; |
| import org.chromium.android_webview.AwBrowserProcess; |
| import org.chromium.android_webview.AwContentsStatics; |
| import org.chromium.android_webview.AwSettings; |
| import org.chromium.android_webview.BrowserSafeModeActionList; |
| import org.chromium.android_webview.ProductConfig; |
| import org.chromium.android_webview.WebViewChromiumRunQueue; |
| import org.chromium.android_webview.common.AwSwitches; |
| import org.chromium.android_webview.common.CommandLineUtil; |
| import org.chromium.android_webview.common.DeveloperModeUtils; |
| import org.chromium.android_webview.common.FlagOverrideHelper; |
| import org.chromium.android_webview.common.ProductionSupportedFlagList; |
| import org.chromium.android_webview.common.SafeModeController; |
| import org.chromium.base.BuildInfo; |
| import org.chromium.base.CommandLine; |
| import org.chromium.base.ContextUtils; |
| import org.chromium.base.Log; |
| import org.chromium.base.PackageUtils; |
| import org.chromium.base.PathUtils; |
| import org.chromium.base.StrictModeContext; |
| import org.chromium.base.ThreadUtils; |
| import org.chromium.base.annotations.VerifiesOnN; |
| import org.chromium.base.annotations.VerifiesOnP; |
| import org.chromium.base.metrics.RecordHistogram; |
| import org.chromium.base.metrics.ScopedSysTraceEvent; |
| import org.chromium.build.BuildConfig; |
| import org.chromium.build.NativeLibraries; |
| import org.chromium.components.embedder_support.application.ClassLoaderContextWrapperFactory; |
| import org.chromium.components.embedder_support.application.FirebaseConfig; |
| import org.chromium.components.version_info.VersionConstants; |
| import org.chromium.content_public.browser.LGEmailActionModeWorkaround; |
| |
| import java.io.File; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.FutureTask; |
| |
| /** |
| * Entry point to the WebView. The system framework talks to this class to get instances of the |
| * implementation classes. |
| */ |
| @SuppressWarnings("deprecation") |
| public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider { |
| private static final String TAG = "WVCFactoryProvider"; |
| |
| private static final String CHROMIUM_PREFS_NAME = "WebViewChromiumPrefs"; |
| private static final String VERSION_CODE_PREF = "lastVersionCodeUsed"; |
| |
| private static final String SUPPORT_LIB_GLUE_AND_BOUNDARY_INTERFACE_PREFIX = |
| "org.chromium.support_lib_"; |
| |
| // This is an ID hardcoded by WebLayer for resources stored in locale splits. See |
| // WebLayerImpl.java for more info. |
| private static final int SHARED_LIBRARY_MAX_ID = 36; |
| |
| /** |
| * This holds objects of classes that are defined in N and above to ensure that run-time class |
| * verification does not occur until it is actually used for N and above. |
| */ |
| @TargetApi(Build.VERSION_CODES.N) |
| @VerifiesOnN |
| private static class ObjectHolderForN { |
| public ServiceWorkerController mServiceWorkerController; |
| } |
| |
| /** |
| * This holds objects of classes that are defined in P and above to ensure that run-time class |
| * verification does not occur until it is actually used for P and above. |
| */ |
| @TargetApi(Build.VERSION_CODES.P) |
| @VerifiesOnP |
| private static class ObjectHolderForP { |
| public TracingController mTracingController; |
| } |
| |
| private static final Object sSingletonLock = new Object(); |
| private static WebViewChromiumFactoryProvider sSingleton; |
| // Used to indicate if WebLayer and WebView are running in the same process. |
| private static boolean sWebLayerRunningInSameProcess; |
| |
| private final WebViewChromiumRunQueue mRunQueue = new WebViewChromiumRunQueue( |
| () -> { return WebViewChromiumFactoryProvider.this.mAwInit.hasStarted(); }); |
| |
| /* package */ WebViewChromiumRunQueue getRunQueue() { |
| return mRunQueue; |
| } |
| |
| // We have a 4 second timeout to try to detect deadlocks to detect and aid in debuggin |
| // deadlocks. |
| // Do not call this method while on the UI thread! |
| /* package */ void runVoidTaskOnUiThreadBlocking(Runnable r) { |
| mRunQueue.runVoidTaskOnUiThreadBlocking(r); |
| } |
| |
| /* package */ <T> T runOnUiThreadBlocking(Callable<T> c) { |
| return mRunQueue.runBlockingFuture(new FutureTask<T>(c)); |
| } |
| |
| /* package */ void addTask(Runnable task) { |
| mRunQueue.addTask(task); |
| } |
| |
| /** |
| * Class that takes care of chromium lazy initialization. |
| * This is package-public so that a downstream subclass can access it. |
| */ |
| /* package */ WebViewChromiumAwInit mAwInit; |
| |
| private SharedPreferences mWebViewPrefs; |
| private WebViewDelegate mWebViewDelegate; |
| |
| protected boolean mShouldDisableThreadChecking; |
| |
| // Initialization guarded by mAwInit.getLock() |
| private Statics mStaticsAdapter; |
| |
| @TargetApi(Build.VERSION_CODES.N) |
| private ObjectHolderForN mObjectHolderForN = |
| Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? new ObjectHolderForN() : null; |
| |
| @TargetApi(Build.VERSION_CODES.P) |
| private ObjectHolderForP mObjectHolderForP = |
| Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? new ObjectHolderForP() : null; |
| |
| /** |
| * Thread-safe way to set the one and only WebViewChromiumFactoryProvider. |
| */ |
| private static void setSingleton(WebViewChromiumFactoryProvider provider) { |
| synchronized (sSingletonLock) { |
| if (sSingleton != null) { |
| throw new RuntimeException( |
| "WebViewChromiumFactoryProvider should only be set once!"); |
| } |
| sSingleton = provider; |
| } |
| } |
| |
| /** |
| * Thread-safe way to get the one and only WebViewChromiumFactoryProvider. |
| */ |
| static WebViewChromiumFactoryProvider getSingleton() { |
| synchronized (sSingletonLock) { |
| if (sSingleton == null) { |
| throw new RuntimeException("WebViewChromiumFactoryProvider has not been set!"); |
| } |
| return sSingleton; |
| } |
| } |
| |
| /** |
| * Entry point for newer versions of Android. |
| */ |
| public static WebViewChromiumFactoryProvider create(android.webkit.WebViewDelegate delegate) { |
| return new WebViewChromiumFactoryProvider(delegate); |
| } |
| |
| /** |
| * Constructor called by the API 21 version of {@link WebViewFactory} and earlier. |
| */ |
| public WebViewChromiumFactoryProvider() { |
| initialize(WebViewDelegateFactory.createApi21CompatibilityDelegate()); |
| } |
| |
| /** |
| * Constructor called by the API 22 version of {@link WebViewFactory} and later. |
| */ |
| public WebViewChromiumFactoryProvider(android.webkit.WebViewDelegate delegate) { |
| initialize(WebViewDelegateFactory.createProxyDelegate(delegate)); |
| } |
| |
| /** |
| * Constructor for internal use when a proxy delegate has already been created. |
| */ |
| WebViewChromiumFactoryProvider(WebViewDelegate delegate) { |
| initialize(delegate); |
| } |
| |
| // Protected to allow downstream to override. |
| protected WebViewChromiumAwInit createAwInit() { |
| try (ScopedSysTraceEvent e2 = |
| ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.createAwInit")) { |
| return new WebViewChromiumAwInit(this); |
| } |
| } |
| |
| // Protected to allow downstream to override. |
| protected ContentSettingsAdapter createContentSettingsAdapter(AwSettings settings) { |
| return new ContentSettingsAdapter(settings); |
| } |
| |
| private void deleteContentsOnPackageDowngrade(PackageInfo packageInfo) { |
| try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped( |
| "WebViewChromiumFactoryProvider.deleteContentsOnPackageDowngrade")) { |
| // Use shared preference to check for package downgrade. |
| // Since N, getSharedPreferences creates the preference dir if it doesn't exist, |
| // causing a disk write. |
| mWebViewPrefs = ContextUtils.getApplicationContext().getSharedPreferences( |
| CHROMIUM_PREFS_NAME, Context.MODE_PRIVATE); |
| int lastVersion = mWebViewPrefs.getInt(VERSION_CODE_PREF, 0); |
| int currentVersion = packageInfo.versionCode; |
| if (!versionCodeGE(currentVersion, lastVersion)) { |
| // The WebView package has been downgraded since we last ran in this |
| // application. Delete the WebView data directory's contents. |
| String dataDir = PathUtils.getDataDirectory(); |
| Log.i(TAG, |
| "WebView package downgraded from " + lastVersion + " to " |
| + currentVersion + "; deleting contents of " + dataDir); |
| deleteContents(new File(dataDir)); |
| } |
| if (lastVersion != currentVersion) { |
| mWebViewPrefs.edit().putInt(VERSION_CODE_PREF, currentVersion).apply(); |
| } |
| } |
| } |
| |
| @SuppressWarnings("NoContextGetApplicationContext") |
| private void initialize(WebViewDelegate webViewDelegate) { |
| long startTime = SystemClock.uptimeMillis(); |
| try (ScopedSysTraceEvent e1 = |
| ScopedSysTraceEvent.scoped("WebViewChromiumFactoryProvider.initialize")) { |
| PackageInfo packageInfo; |
| try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped( |
| "WebViewChromiumFactoryProvider.getLoadedPackageInfo")) { |
| // The package is used to locate the services for copying crash minidumps and |
| // requesting variations seeds. So it must be set before initializing variations and |
| // before a renderer has a chance to crash. |
| packageInfo = WebViewFactory.getLoadedPackageInfo(); |
| } |
| AwBrowserProcess.setWebViewPackageName(packageInfo.packageName); |
| AwBrowserProcess.initializeApkType(packageInfo.applicationInfo); |
| |
| mAwInit = createAwInit(); |
| mWebViewDelegate = webViewDelegate; |
| Context ctx = webViewDelegate.getApplication().getApplicationContext(); |
| |
| // If the application context is DE, but we have credentials, use a CE context instead |
| try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped( |
| "WebViewChromiumFactoryProvider.checkStorage")) { |
| checkStorageIsNotDeviceProtected(webViewDelegate.getApplication()); |
| } catch (IllegalArgumentException e) { |
| assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; |
| if (!GlueApiHelperForN.isUserUnlocked(ctx)) { |
| throw e; |
| } |
| ctx = GlueApiHelperForN.createCredentialProtectedStorageContext(ctx); |
| } |
| |
| // WebView needs to make sure to always use the wrapped application context. |
| ctx = ClassLoaderContextWrapperFactory.get(ctx); |
| ContextUtils.initApplicationContext(ctx); |
| |
| // Find the package ID for the package that WebView's resources come from. |
| // This will be the donor package if there is one, not our main package. |
| String resourcePackage = packageInfo.packageName; |
| if (packageInfo.applicationInfo.metaData != null) { |
| resourcePackage = packageInfo.applicationInfo.metaData.getString( |
| "com.android.webview.WebViewDonorPackage", resourcePackage); |
| } |
| int packageId = webViewDelegate.getPackageId(ctx.getResources(), resourcePackage); |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q |
| && AwBrowserProcess.getApkType() != ApkType.TRICHROME |
| && packageId > SHARED_LIBRARY_MAX_ID) { |
| throw new RuntimeException("Package ID too high for WebView: " + packageId); |
| } |
| |
| mAwInit.setUpResourcesOnBackgroundThread(packageId, ctx); |
| |
| try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped( |
| "WebViewChromiumFactoryProvider.initCommandLine")) { |
| // This may take ~20 ms only on userdebug devices. |
| CommandLineUtil.initCommandLine(); |
| } |
| |
| boolean multiProcess = false; |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
| // Ask the system if multiprocess should be enabled on O+. |
| multiProcess = webViewDelegate.isMultiProcessEnabled(); |
| } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
| // Check the multiprocess developer setting directly on N. |
| multiProcess = Settings.Global.getInt(ctx.getContentResolver(), |
| Settings.Global.WEBVIEW_MULTIPROCESS, 0) |
| == 1; |
| } |
| if (multiProcess) { |
| CommandLine cl = CommandLine.getInstance(); |
| cl.appendSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER); |
| } |
| // Using concatenation rather than %s to allow values to be inlined by R8. |
| Log.i(TAG, |
| "Loaded version=" + VersionConstants.PRODUCT_VERSION + " minSdkVersion=" |
| + BuildConfig.MIN_SDK_VERSION + " isBundle=" + ProductConfig.IS_BUNDLE |
| + " multiprocess=%s packageId=%s", |
| multiProcess, packageId); |
| |
| // Enable modern SameSite cookie behavior if the app targets at least S. |
| if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) { |
| CommandLine cl = CommandLine.getInstance(); |
| cl.appendSwitch(AwSwitches.WEBVIEW_ENABLE_MODERN_COOKIE_SAME_SITE); |
| } |
| |
| int applicationFlags = ctx.getApplicationInfo().flags; |
| boolean isAppDebuggable = (applicationFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; |
| boolean isOsDebuggable = BuildInfo.isDebugAndroid(); |
| // Enable logging JS console messages in system logs only if the app is debuggable or |
| // it's a debugable android build. |
| if (isAppDebuggable || isOsDebuggable) { |
| CommandLine cl = CommandLine.getInstance(); |
| cl.appendSwitch(AwSwitches.WEBVIEW_LOG_JS_CONSOLE_MESSAGES); |
| } |
| |
| String webViewPackageName = AwBrowserProcess.getWebViewPackageName(); |
| long developerModeStart = SystemClock.elapsedRealtime(); |
| boolean isDeveloperModeEnabled = |
| DeveloperModeUtils.isDeveloperModeEnabled(webViewPackageName); |
| long developerModeEnd = SystemClock.elapsedRealtime(); |
| RecordHistogram.recordTimesHistogram("Android.WebView.DevUi.DeveloperModeBlockingTime", |
| developerModeEnd - developerModeStart); |
| RecordHistogram.recordBooleanHistogram( |
| "Android.WebView.DevUi.DeveloperModeEnabled", isDeveloperModeEnabled); |
| Map<String, Boolean> flagOverrides = null; |
| if (isDeveloperModeEnabled) { |
| long start = SystemClock.elapsedRealtime(); |
| try { |
| FlagOverrideHelper helper = |
| new FlagOverrideHelper(ProductionSupportedFlagList.sFlagList); |
| flagOverrides = DeveloperModeUtils.getFlagOverrides(webViewPackageName); |
| helper.applyFlagOverrides(flagOverrides); |
| |
| RecordHistogram.recordCount100Histogram( |
| "Android.WebView.DevUi.ToggledFlagCount", flagOverrides.size()); |
| } finally { |
| long end = SystemClock.elapsedRealtime(); |
| RecordHistogram.recordTimesHistogram( |
| "Android.WebView.DevUi.FlagLoadingBlockingTime", end - start); |
| } |
| } |
| |
| ThreadUtils.setWillOverrideUiThread(true); |
| BuildInfo.setBrowserPackageInfo(packageInfo); |
| BuildInfo.setFirebaseAppId( |
| FirebaseConfig.getFirebaseAppIdForPackage(packageInfo.packageName)); |
| |
| try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) { |
| try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped( |
| "WebViewChromiumFactoryProvider.loadChromiumLibrary")) { |
| String dataDirectorySuffix = null; |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { |
| dataDirectorySuffix = webViewDelegate.getDataDirectorySuffix(); |
| } |
| AwBrowserProcess.loadLibrary(dataDirectorySuffix); |
| } |
| |
| try (ScopedSysTraceEvent e2 = ScopedSysTraceEvent.scoped( |
| "WebViewChromiumFactoryProvider.loadGlueLayerPlatSupportLibrary")) { |
| System.loadLibrary("webviewchromium_plat_support"); |
| } |
| |
| deleteContentsOnPackageDowngrade(packageInfo); |
| } |
| |
| // Now safe to use WebView data directory. |
| |
| if (flagOverrides != null) { |
| AwContentsStatics.logFlagOverridesWithNative(flagOverrides); |
| } |
| |
| SafeModeController controller = SafeModeController.getInstance(); |
| controller.registerActions(BrowserSafeModeActionList.sList); |
| long safeModeStart = SystemClock.elapsedRealtime(); |
| boolean isSafeModeEnabled = controller.isSafeModeEnabled(webViewPackageName); |
| long safeModeEnd = SystemClock.elapsedRealtime(); |
| RecordHistogram.recordTimesHistogram( |
| "Android.WebView.SafeMode.CheckStateBlockingTime", safeModeEnd - safeModeStart); |
| RecordHistogram.recordBooleanHistogram( |
| "Android.WebView.SafeMode.SafeModeEnabled", isSafeModeEnabled); |
| if (isSafeModeEnabled) { |
| try { |
| long safeModeQueryExecuteStart = SystemClock.elapsedRealtime(); |
| Set<String> actions = controller.queryActions(webViewPackageName); |
| Log.w(TAG, "WebViewSafeMode is enabled: received %d SafeModeActions", |
| actions.size()); |
| RecordHistogram.recordCount100Histogram( |
| "Android.WebView.SafeMode.ActionsCount", actions.size()); |
| controller.executeActions(actions); |
| long safeModeQueryExecuteEnd = SystemClock.elapsedRealtime(); |
| RecordHistogram.recordTimesHistogram( |
| "Android.WebView.SafeMode.QueryAndExecuteBlockingTime", |
| safeModeQueryExecuteEnd - safeModeQueryExecuteStart); |
| logSafeModeExecutionResult(SafeModeExecutionResult.SUCCESS); |
| } catch (Throwable t) { |
| // Don't let SafeMode crash WebView. Instead just log the error. |
| Log.e(TAG, "WebViewSafeMode threw exception: ", t); |
| logSafeModeExecutionResult(SafeModeExecutionResult.UNKNOWN_ERROR); |
| } |
| } |
| |
| mAwInit.startVariationsInit(); |
| |
| mShouldDisableThreadChecking = shouldDisableThreadChecking(ctx); |
| |
| setSingleton(this); |
| |
| // sWebLayerRunningInSameProcess may have been set before initialize(). |
| if (sWebLayerRunningInSameProcess) { |
| addTask(() -> { getBrowserContextOnUiThread().setWebLayerRunningInSameProcess(); }); |
| } |
| } |
| |
| RecordHistogram.recordTimesHistogram( |
| "Android.WebView.Startup.CreationTime.Stage1.FactoryInit", |
| SystemClock.uptimeMillis() - startTime); |
| |
| /* TODO(torne): re-enable this once the API change is sorted out |
| if (BuildInfo.isAtLeastS()) { |
| // TODO: Use the framework constants as indices in timestamps array. |
| startTime = mWebViewDelegate.getTimestamps()[0]; |
| RecordHistogram.recordTimesHistogram( |
| "Android.WebView.Startup.CreationTime.TotalFactoryInitTime", |
| SystemClock.uptimeMillis() - startTime); |
| } |
| */ |
| } |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| @IntDef({SafeModeExecutionResult.SUCCESS, SafeModeExecutionResult.UNKNOWN_ERROR}) |
| private @interface SafeModeExecutionResult { |
| int SUCCESS = 0; |
| int UNKNOWN_ERROR = 1; |
| int COUNT = 2; |
| } |
| |
| private static void logSafeModeExecutionResult(@SafeModeExecutionResult int result) { |
| RecordHistogram.recordEnumeratedHistogram( |
| "Android.WebView.SafeMode.ExecutionResult", result, SafeModeExecutionResult.COUNT); |
| } |
| |
| /* package */ static void checkStorageIsNotDeviceProtected(Context context) { |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N |
| && GlueApiHelperForN.isDeviceProtectedStorage(context)) { |
| throw new IllegalArgumentException( |
| "WebView cannot be used with device protected storage"); |
| } |
| } |
| |
| /** |
| * Both versionCodes should be from a WebView provider package implemented by Chromium. |
| * VersionCodes from other kinds of packages won't make any sense in this method. |
| * |
| * An introduction to Chromium versionCode scheme: |
| * "BBBBPPPAX" |
| * BBBB: 4 digit branch number. It monotonically increases over time. |
| * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits may |
| * change their meaning in the future. |
| * A: architecture digit. |
| * X: A digit to differentiate APKs for other reasons. |
| * |
| * This method takes the "BBBB" of versionCodes and compare them. |
| * |
| * @return true if versionCode1 is higher than or equal to versionCode2. |
| */ |
| private static boolean versionCodeGE(int versionCode1, int versionCode2) { |
| int v1 = versionCode1 / 100000; |
| int v2 = versionCode2 / 100000; |
| |
| return v1 >= v2; |
| } |
| |
| private static void deleteContents(File dir) { |
| File[] files = dir.listFiles(); |
| if (files != null) { |
| for (File file : files) { |
| if (file.isDirectory()) { |
| deleteContents(file); |
| } |
| if (!file.delete()) { |
| Log.w(TAG, "Failed to delete " + file); |
| } |
| } |
| } |
| } |
| |
| public static boolean preloadInZygote() { |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O |
| && Build.VERSION.SDK_INT < Build.VERSION_CODES.P && ProductConfig.IS_BUNDLE) { |
| // Apply workaround if we're a bundle on O, where the split APK handling bug exists. |
| SplitApkWorkaround.apply(); |
| } |
| |
| for (String library : NativeLibraries.LIBRARIES) { |
| System.loadLibrary(library); |
| } |
| return true; |
| } |
| |
| SharedPreferences getWebViewPrefs() { |
| return mWebViewPrefs; |
| } |
| |
| @Override |
| public Statics getStatics() { |
| synchronized (mAwInit.getLock()) { |
| SharedStatics sharedStatics = mAwInit.getStatics(); |
| if (mStaticsAdapter == null) { |
| mStaticsAdapter = new WebViewChromiumFactoryProvider.Statics() { |
| @Override |
| public String findAddress(String addr) { |
| return sharedStatics.findAddress(addr); |
| } |
| |
| @Override |
| public String getDefaultUserAgent(Context context) { |
| return sharedStatics.getDefaultUserAgent(context); |
| } |
| |
| @Override |
| public void setWebContentsDebuggingEnabled(boolean enable) { |
| sharedStatics.setWebContentsDebuggingEnabled(enable); |
| } |
| |
| @Override |
| public void clearClientCertPreferences(Runnable onCleared) { |
| sharedStatics.clearClientCertPreferences(onCleared); |
| } |
| |
| @Override |
| public void freeMemoryForTests() { |
| sharedStatics.freeMemoryForTests(); |
| } |
| |
| @Override |
| public void enableSlowWholeDocumentDraw() { |
| sharedStatics.enableSlowWholeDocumentDraw(); |
| } |
| |
| @Override |
| public Uri[] parseFileChooserResult(int resultCode, Intent intent) { |
| return sharedStatics.parseFileChooserResult(resultCode, intent); |
| } |
| |
| @Override |
| public void initSafeBrowsing(Context context, ValueCallback<Boolean> callback) { |
| sharedStatics.initSafeBrowsing( |
| context, CallbackConverter.fromValueCallback(callback)); |
| } |
| |
| @Override |
| public void setSafeBrowsingWhitelist( |
| List<String> urls, ValueCallback<Boolean> callback) { |
| sharedStatics.setSafeBrowsingAllowlist( |
| urls, CallbackConverter.fromValueCallback(callback)); |
| } |
| |
| @Override |
| public Uri getSafeBrowsingPrivacyPolicyUrl() { |
| return sharedStatics.getSafeBrowsingPrivacyPolicyUrl(); |
| } |
| |
| public boolean isMultiProcessEnabled() { |
| return sharedStatics.isMultiProcessEnabled(); |
| } |
| }; |
| } |
| } |
| return mStaticsAdapter; |
| } |
| |
| @Override |
| public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) { |
| return new WebViewChromium(this, webView, privateAccess, mShouldDisableThreadChecking); |
| } |
| |
| // Workaround for IME thread crashes on legacy OEM apps. |
| private boolean shouldDisableThreadChecking(Context context) { |
| String appName = context.getPackageName(); |
| int versionCode = PackageUtils.getPackageVersion(context, appName); |
| int appTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; |
| if (versionCode == -1) return false; |
| |
| boolean shouldDisable = false; |
| |
| // crbug.com/651706 |
| final String lgeMailPackageId = "com.lge.email"; |
| if (lgeMailPackageId.equals(appName)) { |
| if (appTargetSdkVersion > Build.VERSION_CODES.N) return false; |
| if (LGEmailActionModeWorkaround.isSafeVersion(versionCode)) return false; |
| shouldDisable = true; |
| } |
| |
| // crbug.com/655759 |
| // Also want to cover ".att" variant suffix package name. |
| final String yahooMailPackageId = "com.yahoo.mobile.client.android.mail"; |
| if (appName.startsWith(yahooMailPackageId)) { |
| if (appTargetSdkVersion > Build.VERSION_CODES.M) return false; |
| if (versionCode > 1315850) return false; |
| shouldDisable = true; |
| } |
| |
| // crbug.com/622151 |
| final String htcMailPackageId = "com.htc.android.mail"; |
| if (htcMailPackageId.equals(appName)) { |
| if (appTargetSdkVersion > Build.VERSION_CODES.M) return false; |
| // This value is provided by HTC. |
| if (versionCode >= 866001861) return false; |
| shouldDisable = true; |
| } |
| |
| if (shouldDisable) { |
| Log.w(TAG, "Disabling thread check in WebView. " |
| + "APK name: " + appName + ", versionCode: " + versionCode |
| + ", targetSdkVersion: " + appTargetSdkVersion); |
| } |
| return shouldDisable; |
| } |
| |
| @Override |
| public GeolocationPermissions getGeolocationPermissions() { |
| return mAwInit.getGeolocationPermissions(); |
| } |
| |
| @Override |
| public CookieManager getCookieManager() { |
| return mAwInit.getCookieManager(); |
| } |
| |
| @Override |
| public ServiceWorkerController getServiceWorkerController() { |
| synchronized (mAwInit.getLock()) { |
| if (mObjectHolderForN.mServiceWorkerController == null) { |
| mObjectHolderForN.mServiceWorkerController = |
| GlueApiHelperForN.createServiceWorkerControllerAdapter(mAwInit); |
| } |
| } |
| return mObjectHolderForN.mServiceWorkerController; |
| } |
| |
| @Override |
| public TokenBindingService getTokenBindingService() { |
| return null; |
| } |
| |
| @Override |
| public android.webkit.WebIconDatabase getWebIconDatabase() { |
| return mAwInit.getWebIconDatabase(); |
| } |
| |
| @Override |
| public WebStorage getWebStorage() { |
| return mAwInit.getWebStorage(); |
| } |
| |
| @Override |
| public WebViewDatabase getWebViewDatabase(final Context context) { |
| return mAwInit.getWebViewDatabase(context); |
| } |
| |
| WebViewDelegate getWebViewDelegate() { |
| return mWebViewDelegate; |
| } |
| |
| WebViewContentsClientAdapter createWebViewContentsClientAdapter(WebView webView, |
| Context context) { |
| try (ScopedSysTraceEvent e = ScopedSysTraceEvent.scoped( |
| "WebViewChromiumFactoryProvider.insideCreateWebViewContentsClientAdapter")) { |
| return new WebViewContentsClientAdapter(webView, context, mWebViewDelegate); |
| } |
| } |
| |
| void startYourEngines(boolean onMainThread) { |
| try (ScopedSysTraceEvent e1 = ScopedSysTraceEvent.scoped( |
| "WebViewChromiumFactoryProvider.startYourEngines")) { |
| mAwInit.startYourEngines(onMainThread); |
| } |
| } |
| |
| boolean hasStarted() { |
| return mAwInit.hasStarted(); |
| } |
| |
| // Only on UI thread. |
| AwBrowserContext getBrowserContextOnUiThread() { |
| return mAwInit.getBrowserContextOnUiThread(); |
| } |
| |
| WebViewChromiumAwInit getAwInit() { |
| return mAwInit; |
| } |
| |
| @Override |
| public TracingController getTracingController() { |
| synchronized (mAwInit.getLock()) { |
| mAwInit.ensureChromiumStartedLocked(true); |
| // ensureChromiumStartedLocked() can release the lock on first call while |
| // waiting for startup. Hence check the mTracingController here to ensure |
| // the singleton property. |
| if (mObjectHolderForP.mTracingController == null) { |
| mObjectHolderForP.mTracingController = |
| GlueApiHelperForP.createTracingControllerAdapter(this, mAwInit); |
| } |
| } |
| return mObjectHolderForP.mTracingController; |
| } |
| |
| private static class FilteredClassLoader extends ClassLoader { |
| FilteredClassLoader(ClassLoader delegate) { |
| super(delegate); |
| } |
| |
| @Override |
| protected Class<?> findClass(String name) throws ClassNotFoundException { |
| final String message = |
| "This ClassLoader should only be used for the androidx.webkit support library"; |
| |
| if (name == null) { |
| throw new ClassNotFoundException(message); |
| } |
| |
| // We only permit this ClassLoader to load classes required for support library |
| // reflection, as applications should not use this for any other purpose. So, we permit |
| // anything in the support_lib_glue and support_lib_boundary packages (and their |
| // subpackages). |
| if (name.startsWith(SUPPORT_LIB_GLUE_AND_BOUNDARY_INTERFACE_PREFIX)) { |
| return super.findClass(name); |
| } |
| |
| throw new ClassNotFoundException(message); |
| } |
| } |
| |
| @Override |
| public ClassLoader getWebViewClassLoader() { |
| return new FilteredClassLoader(WebViewChromiumFactoryProvider.class.getClassLoader()); |
| } |
| |
| // This is called from WebLayer when WebView and WebLayer are run in the same process. It's |
| // used to set a crash key to help attribute crashes. It's entirely possible WebView has not |
| // been initialized when this is called. |
| public static void setWebLayerRunningInSameProcess() { |
| // This may be called before initialize(). |
| synchronized (sSingletonLock) { |
| sWebLayerRunningInSameProcess = true; |
| if (sSingleton == null) { |
| // initialize() hasn't been called yet. When initialize() is called |
| // |sWebLayerRunningInSameProcess| will be checked. |
| return; |
| } |
| } |
| getSingleton().addTask(() -> { |
| getSingleton().getBrowserContextOnUiThread().setWebLayerRunningInSameProcess(); |
| }); |
| } |
| |
| @Override |
| public PacProcessor getPacProcessor() { |
| return GlueApiHelperForR.getPacProcessor(); |
| } |
| |
| @Override |
| public PacProcessor createPacProcessor() { |
| return GlueApiHelperForR.createPacProcessor(); |
| } |
| } |