1 // Copyright 2014 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.
5 package com
.android
.webview
.chromium
;
7 import android
.app
.ActivityManager
;
8 import android
.content
.ComponentCallbacks2
;
9 import android
.content
.Context
;
10 import android
.content
.Intent
;
11 import android
.content
.SharedPreferences
;
12 import android
.content
.pm
.PackageInfo
;
13 import android
.net
.Uri
;
14 import android
.os
.Build
;
15 import android
.os
.Looper
;
16 import android
.os
.StrictMode
;
17 import android
.util
.Log
;
18 import android
.webkit
.CookieManager
;
19 import android
.webkit
.GeolocationPermissions
;
20 import android
.webkit
.WebStorage
;
21 import android
.webkit
.WebView
;
22 import android
.webkit
.WebViewDatabase
;
23 import android
.webkit
.WebViewFactory
;
24 import android
.webkit
.WebViewFactoryProvider
;
25 import android
.webkit
.WebViewProvider
;
27 import com
.android
.webview
.chromium
.WebViewDelegateFactory
.WebViewDelegate
;
29 import org
.chromium
.android_webview
.AwBrowserContext
;
30 import org
.chromium
.android_webview
.AwBrowserProcess
;
31 import org
.chromium
.android_webview
.AwContents
;
32 import org
.chromium
.android_webview
.AwContentsClient
;
33 import org
.chromium
.android_webview
.AwContentsStatics
;
34 import org
.chromium
.android_webview
.AwCookieManager
;
35 import org
.chromium
.android_webview
.AwDataReductionProxyManager
;
36 import org
.chromium
.android_webview
.AwDevToolsServer
;
37 import org
.chromium
.android_webview
.AwQuotaManagerBridge
;
38 import org
.chromium
.android_webview
.AwResource
;
39 import org
.chromium
.android_webview
.AwSettings
;
40 import org
.chromium
.base
.CommandLine
;
41 import org
.chromium
.base
.MemoryPressureListener
;
42 import org
.chromium
.base
.PathService
;
43 import org
.chromium
.base
.PathUtils
;
44 import org
.chromium
.base
.ResourceExtractor
;
45 import org
.chromium
.base
.ThreadUtils
;
46 import org
.chromium
.base
.TraceEvent
;
47 import org
.chromium
.base
.library_loader
.LibraryLoader
;
48 import org
.chromium
.base
.library_loader
.LibraryProcessType
;
49 import org
.chromium
.base
.library_loader
.ProcessInitException
;
50 import org
.chromium
.content
.app
.ContentMain
;
51 import org
.chromium
.content
.browser
.ContentViewStatics
;
54 import java
.lang
.ref
.WeakReference
;
55 import java
.util
.ArrayList
;
58 * Entry point to the WebView. The system framework talks to this class to get instances of the
59 * implementation classes.
61 @SuppressWarnings("deprecation")
62 public class WebViewChromiumFactoryProvider
implements WebViewFactoryProvider
{
63 private static final String TAG
= "WebViewChromiumFactoryProvider";
65 private static final String CHROMIUM_PREFS_NAME
= "WebViewChromiumPrefs";
66 private static final String VERSION_CODE_PREF
= "lastVersionCodeUsed";
67 private static final String COMMAND_LINE_FILE
= "/data/local/tmp/webview-command-line";
69 // Guards accees to the other members, and is notifyAll() signalled on the UI thread
70 // when the chromium process has been started.
71 private final Object mLock
= new Object();
73 // Initialization guarded by mLock.
74 private AwBrowserContext mBrowserContext
;
75 private Statics mStaticMethods
;
76 private GeolocationPermissionsAdapter mGeolocationPermissions
;
77 private CookieManagerAdapter mCookieManager
;
78 private WebIconDatabaseAdapter mWebIconDatabase
;
79 private WebStorageAdapter mWebStorage
;
80 private WebViewDatabaseAdapter mWebViewDatabase
;
81 private AwDevToolsServer mDevToolsServer
;
82 private Context mWrappedAppContext
;
84 private ArrayList
<WeakReference
<WebViewChromium
>> mWebViewsToStart
=
85 new ArrayList
<WeakReference
<WebViewChromium
>>();
87 // Read/write protected by mLock.
88 private boolean mStarted
;
89 private AwDataReductionProxyManager mProxyManager
;
91 private SharedPreferences mWebViewPrefs
;
92 private WebViewDelegate mWebViewDelegate
;
95 * Constructor called by the API 21 version of {@link WebViewFactory} and earlier.
97 public WebViewChromiumFactoryProvider() {
98 initialize(WebViewDelegateFactory
.createApi21CompatibilityDelegate());
102 * Constructor called by the API 22 version of {@link WebViewFactory} and later.
104 public WebViewChromiumFactoryProvider(android
.webkit
.WebViewDelegate delegate
) {
105 initialize(WebViewDelegateFactory
.createProxyDelegate(delegate
));
108 private void initialize(WebViewDelegate webViewDelegate
) {
109 mWebViewDelegate
= webViewDelegate
;
110 if (isBuildDebuggable()) {
111 // Suppress the StrictMode violation as this codepath is only hit on debugglable builds.
112 StrictMode
.ThreadPolicy oldPolicy
= StrictMode
.allowThreadDiskReads();
113 CommandLine
.initFromFile(COMMAND_LINE_FILE
);
114 StrictMode
.setThreadPolicy(oldPolicy
);
116 CommandLine
.init(null);
119 CommandLine cl
= CommandLine
.getInstance();
120 // TODO: currently in a relase build the DCHECKs only log. We either need to insall
121 // a report handler with SetLogReportHandler to make them assert, or else compile
122 // them out of the build altogether (b/8284203). Either way, so long they're
123 // compiled in, we may as unconditionally enable them here.
124 cl
.appendSwitch("enable-dcheck");
126 ThreadUtils
.setWillOverrideUiThread();
127 // Load chromium library.
128 AwBrowserProcess
.loadLibrary(getWrappedCurrentApplicationContext());
130 final PackageInfo packageInfo
= WebViewFactory
.getLoadedPackageInfo();
132 // Register the handler that will append the WebView version to logcat in case of a crash.
133 AwContentsStatics
.registerCrashHandler(
134 "Version " + packageInfo
.versionName
+ " (code " + packageInfo
.versionCode
+ ")");
136 // Load glue-layer support library.
137 System
.loadLibrary("webviewchromium_plat_support");
139 // Use shared preference to check for package downgrade.
140 mWebViewPrefs
= mWebViewDelegate
.getApplication().getSharedPreferences(
141 CHROMIUM_PREFS_NAME
, Context
.MODE_PRIVATE
);
142 int lastVersion
= mWebViewPrefs
.getInt(VERSION_CODE_PREF
, 0);
143 int currentVersion
= packageInfo
.versionCode
;
144 if (lastVersion
> currentVersion
) {
145 // The WebView package has been downgraded since we last ran in this application.
146 // Delete the WebView data directory's contents.
147 String dataDir
= PathUtils
.getDataDirectory(mWebViewDelegate
.getApplication());
148 Log
.i(TAG
, "WebView package downgraded from " + lastVersion
+ " to " + currentVersion
149 + "; deleting contents of " + dataDir
);
150 deleteContents(new File(dataDir
));
152 if (lastVersion
!= currentVersion
) {
153 mWebViewPrefs
.edit().putInt(VERSION_CODE_PREF
, currentVersion
).apply();
155 // Now safe to use WebView data directory.
158 private static boolean isBuildDebuggable() {
159 return !Build
.TYPE
.equals("user");
162 private static void deleteContents(File dir
) {
163 File
[] files
= dir
.listFiles();
165 for (File file
: files
) {
166 if (file
.isDirectory()) {
167 deleteContents(file
);
169 if (!file
.delete()) {
170 Log
.w(TAG
, "Failed to delete " + file
);
176 private void initPlatSupportLibrary() {
177 DrawGLFunctor
.setChromiumAwDrawGLFunction(AwContents
.getAwDrawGLFunction());
178 AwContents
.setAwDrawSWFunctionTable(GraphicsUtils
.getDrawSWFunctionTable());
179 AwContents
.setAwDrawGLFunctionTable(GraphicsUtils
.getDrawGLFunctionTable());
182 private void ensureChromiumStartedLocked(boolean onMainThread
) {
183 assert Thread
.holdsLock(mLock
);
185 if (mStarted
) { // Early-out for the common case.
189 Looper looper
= !onMainThread ? Looper
.myLooper() : Looper
.getMainLooper();
190 Log
.v(TAG
, "Binding Chromium to "
191 + (Looper
.getMainLooper().equals(looper
) ?
"main" : "background")
192 + " looper " + looper
);
193 ThreadUtils
.setUiThread(looper
);
195 if (ThreadUtils
.runningOnUiThread()) {
196 startChromiumLocked();
200 // We must post to the UI thread to cover the case that the user has invoked Chromium
201 // startup by using the (thread-safe) CookieManager rather than creating a WebView.
202 ThreadUtils
.postOnUiThread(new Runnable() {
205 synchronized (mLock
) {
206 startChromiumLocked();
212 // Important: wait() releases |mLock| the UI thread can take it :-)
214 } catch (InterruptedException e
) {
215 // Keep trying... eventually the UI thread will process the task we sent it.
220 // TODO: DIR_RESOURCE_PAKS_ANDROID needs to live somewhere sensible,
221 // inlined here for simplicity setting up the HTMLViewer demo. Unfortunately
222 // it can't go into base.PathService, as the native constant it refers to
223 // lives in the ui/ layer. See ui/base/ui_base_paths.h
224 private static final int DIR_RESOURCE_PAKS_ANDROID
= 3003;
226 private void startChromiumLocked() {
227 assert Thread
.holdsLock(mLock
) && ThreadUtils
.runningOnUiThread();
229 // The post-condition of this method is everything is ready, so notify now to cover all
230 // return paths. (Other threads will not wake-up until we release |mLock|, whatever).
237 // We don't need to extract any paks because for WebView, they are
238 // in the system image.
239 ResourceExtractor
.setMandatoryPaksToExtract("");
242 LibraryLoader
.get(LibraryProcessType
.PROCESS_WEBVIEW
).ensureInitialized();
243 } catch (ProcessInitException e
) {
244 throw new RuntimeException("Error initializing WebView library", e
);
247 PathService
.override(PathService
.DIR_MODULE
, "/system/lib/");
248 PathService
.override(DIR_RESOURCE_PAKS_ANDROID
, "/system/framework/webview/paks");
250 // Make sure that ResourceProvider is initialized before starting the browser process.
251 Context context
= getWrappedCurrentApplicationContext();
252 setUpResources(context
);
253 initPlatSupportLibrary();
254 AwBrowserProcess
.start(context
);
256 if (isBuildDebuggable()) {
257 setWebContentsDebuggingEnabled(true);
260 TraceEvent
.setATraceEnabled(mWebViewDelegate
.isTraceTagEnabled());
261 mWebViewDelegate
.setOnTraceEnabledChangeListener(
262 new WebViewDelegate
.OnTraceEnabledChangeListener() {
264 public void onTraceEnabledChange(boolean enabled
) {
265 TraceEvent
.setATraceEnabled(enabled
);
270 for (WeakReference
<WebViewChromium
> wvc
: mWebViewsToStart
) {
271 WebViewChromium w
= wvc
.get();
276 mWebViewsToStart
.clear();
277 mWebViewsToStart
= null;
279 // Start listening for data reduction proxy setting changes.
280 mProxyManager
= new AwDataReductionProxyManager();
281 mProxyManager
.start(mWebViewDelegate
.getApplication());
284 boolean hasStarted() {
288 void startYourEngines(boolean onMainThread
) {
289 synchronized (mLock
) {
290 ensureChromiumStartedLocked(onMainThread
);
294 private Context
getWrappedCurrentApplicationContext() {
295 if (mWrappedAppContext
== null) {
296 mWrappedAppContext
= ResourcesContextWrapperFactory
.get(
297 mWebViewDelegate
.getApplication());
299 return mWrappedAppContext
;
302 AwBrowserContext
getBrowserContext() {
303 synchronized (mLock
) {
304 return getBrowserContextLocked();
308 private AwBrowserContext
getBrowserContextLocked() {
309 assert Thread
.holdsLock(mLock
);
311 if (mBrowserContext
== null) {
312 mBrowserContext
= new AwBrowserContext(mWebViewPrefs
);
314 return mBrowserContext
;
317 private void setWebContentsDebuggingEnabled(boolean enable
) {
318 if (Looper
.myLooper() != ThreadUtils
.getUiThreadLooper()) {
319 throw new RuntimeException(
320 "Toggling of Web Contents Debugging must be done on the UI thread");
322 if (mDevToolsServer
== null) {
324 mDevToolsServer
= new AwDevToolsServer();
326 mDevToolsServer
.setRemoteDebuggingEnabled(enable
);
329 private void setUpResources(Context context
) {
330 final String packageName
= WebViewFactory
.getLoadedPackageInfo().packageName
;
331 ResourceRewriter
.rewriteRValues(
332 mWebViewDelegate
.getPackageId(context
.getResources(), packageName
));
334 AwResource
.setResources(context
.getResources());
335 AwResource
.setErrorPageResources(android
.R
.raw
.loaderror
, android
.R
.raw
.nodomain
);
336 AwResource
.setConfigKeySystemUuidMapping(android
.R
.array
.config_keySystemUuidMapping
);
340 public Statics
getStatics() {
341 synchronized (mLock
) {
342 if (mStaticMethods
== null) {
343 // TODO: Optimization potential: most these methods only need the native library
344 // loaded and initialized, not the entire browser process started.
345 // See also http://b/7009882
346 ensureChromiumStartedLocked(true);
347 mStaticMethods
= new WebViewFactoryProvider
.Statics() {
349 public String
findAddress(String addr
) {
350 return ContentViewStatics
.findAddress(addr
);
354 public String
getDefaultUserAgent(Context context
) {
355 return AwSettings
.getDefaultUserAgent();
359 public void setWebContentsDebuggingEnabled(boolean enable
) {
360 // Web Contents debugging is always enabled on debug builds.
361 if (!isBuildDebuggable()) {
362 WebViewChromiumFactoryProvider
.this.setWebContentsDebuggingEnabled(
367 // TODO enable after L release to AOSP
369 public void clearClientCertPreferences(Runnable onCleared
) {
370 AwContentsStatics
.clearClientCertPreferences(onCleared
);
374 public void freeMemoryForTests() {
375 if (ActivityManager
.isRunningInTestHarness()) {
376 MemoryPressureListener
.maybeNotifyMemoryPresure(
377 ComponentCallbacks2
.TRIM_MEMORY_COMPLETE
);
381 // TODO: Add @Override.
382 public void enableSlowWholeDocumentDraw() {
383 WebViewChromium
.enableSlowWholeDocumentDraw();
387 public Uri
[] parseFileChooserResult(int resultCode
, Intent intent
) {
388 return AwContentsClient
.parseFileChooserResult(resultCode
, intent
);
393 return mStaticMethods
;
397 public WebViewProvider
createWebView(WebView webView
, WebView
.PrivateAccess privateAccess
) {
398 WebViewChromium wvc
= new WebViewChromium(this, webView
, privateAccess
);
400 synchronized (mLock
) {
401 if (mWebViewsToStart
!= null) {
402 mWebViewsToStart
.add(new WeakReference
<WebViewChromium
>(wvc
));
410 public GeolocationPermissions
getGeolocationPermissions() {
411 synchronized (mLock
) {
412 if (mGeolocationPermissions
== null) {
413 ensureChromiumStartedLocked(true);
414 mGeolocationPermissions
= new GeolocationPermissionsAdapter(
415 getBrowserContextLocked().getGeolocationPermissions());
418 return mGeolocationPermissions
;
422 public CookieManager
getCookieManager() {
423 synchronized (mLock
) {
424 if (mCookieManager
== null) {
426 // We can use CookieManager without starting Chromium; the native code
427 // will bring up just the parts it needs to make this work on a temporary
428 // basis until Chromium is started for real. The temporary cookie manager
429 // needs the application context to have been set.
430 ContentMain
.initApplicationContext(getWrappedCurrentApplicationContext());
432 mCookieManager
= new CookieManagerAdapter(new AwCookieManager());
435 return mCookieManager
;
439 public android
.webkit
.WebIconDatabase
getWebIconDatabase() {
440 synchronized (mLock
) {
441 if (mWebIconDatabase
== null) {
442 ensureChromiumStartedLocked(true);
443 mWebIconDatabase
= new WebIconDatabaseAdapter();
446 return mWebIconDatabase
;
450 public WebStorage
getWebStorage() {
451 synchronized (mLock
) {
452 if (mWebStorage
== null) {
453 ensureChromiumStartedLocked(true);
454 mWebStorage
= new WebStorageAdapter(AwQuotaManagerBridge
.getInstance());
461 public WebViewDatabase
getWebViewDatabase(Context context
) {
462 synchronized (mLock
) {
463 if (mWebViewDatabase
== null) {
464 ensureChromiumStartedLocked(true);
465 AwBrowserContext browserContext
= getBrowserContextLocked();
466 mWebViewDatabase
= new WebViewDatabaseAdapter(browserContext
.getFormDatabase(),
467 browserContext
.getHttpAuthDatabase(context
));
470 return mWebViewDatabase
;
473 WebViewDelegate
getWebViewDelegate() {
474 return mWebViewDelegate
;