WebViewChromiumFactoryProvider.java revision 527e42504f0b418492e56871b9539501feda90e6
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.webview.chromium; 18 19import android.content.pm.PackageManager; 20import android.content.res.Resources; 21import android.app.ActivityManager; 22import android.app.ActivityThread; 23import android.content.ComponentCallbacks2; 24import android.content.Context; 25import android.content.SharedPreferences; 26import android.os.Build; 27import android.os.Looper; 28import android.os.SystemProperties; 29import android.os.Trace; 30import android.util.Log; 31import android.webkit.CookieManager; 32import android.webkit.GeolocationPermissions; 33import android.webkit.WebIconDatabase; 34import android.webkit.WebStorage; 35import android.webkit.WebView; 36import android.webkit.WebViewDatabase; 37import android.webkit.WebViewFactoryProvider; 38import android.webkit.WebViewProvider; 39 40import org.chromium.android_webview.AwBrowserContext; 41import org.chromium.android_webview.AwBrowserProcess; 42import org.chromium.android_webview.AwContents; 43import org.chromium.android_webview.AwContentsStatics; 44import org.chromium.android_webview.AwCookieManager; 45import org.chromium.android_webview.AwDevToolsServer; 46import org.chromium.android_webview.AwFormDatabase; 47import org.chromium.android_webview.AwGeolocationPermissions; 48import org.chromium.android_webview.AwQuotaManagerBridge; 49import org.chromium.android_webview.AwResource; 50import org.chromium.android_webview.AwSettings; 51import org.chromium.base.CommandLine; 52import org.chromium.base.MemoryPressureListener; 53import org.chromium.base.PathService; 54import org.chromium.base.ThreadUtils; 55import org.chromium.base.TraceEvent; 56import org.chromium.base.library_loader.LibraryLoader; 57import org.chromium.base.library_loader.ProcessInitException; 58import org.chromium.content.app.ContentMain; 59import org.chromium.content.browser.ContentViewStatics; 60import org.chromium.content.browser.ResourceExtractor; 61 62import java.lang.ref.WeakReference; 63import java.util.ArrayList; 64 65public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider { 66 67 private static final String TAG = "WebViewChromiumFactoryProvider"; 68 69 private static final String CHROMIUM_PREFS_NAME = "WebViewChromiumPrefs"; 70 private static final String COMMAND_LINE_FILE = "/data/local/tmp/webview-command-line"; 71 72 // Guards accees to the other members, and is notifyAll() signalled on the UI thread 73 // when the chromium process has been started. 74 private final Object mLock = new Object(); 75 76 // Initialization guarded by mLock. 77 private AwBrowserContext mBrowserContext; 78 private Statics mStaticMethods; 79 private GeolocationPermissionsAdapter mGeolocationPermissions; 80 private CookieManagerAdapter mCookieManager; 81 private WebIconDatabaseAdapter mWebIconDatabase; 82 private WebStorageAdapter mWebStorage; 83 private WebViewDatabaseAdapter mWebViewDatabase; 84 private AwDevToolsServer mDevToolsServer; 85 86 private ArrayList<WeakReference<WebViewChromium>> mWebViewsToStart = 87 new ArrayList<WeakReference<WebViewChromium>>(); 88 89 // Read/write protected by mLock. 90 private boolean mStarted; 91 92 private DataReductionProxyManager mProxyManager; 93 94 public WebViewChromiumFactoryProvider() { 95 ThreadUtils.setWillOverrideUiThread(); 96 // Load chromium library. 97 AwBrowserProcess.loadLibrary(); 98 // Load glue-layer support library. 99 System.loadLibrary("webviewchromium_plat_support"); 100 } 101 102 private void initPlatSupportLibrary() { 103 DrawGLFunctor.setChromiumAwDrawGLFunction(AwContents.getAwDrawGLFunction()); 104 AwContents.setAwDrawSWFunctionTable(GraphicsUtils.getDrawSWFunctionTable()); 105 AwContents.setAwDrawGLFunctionTable(GraphicsUtils.getDrawGLFunctionTable()); 106 } 107 108 private static void initTraceEvent() { 109 syncATraceState(); 110 SystemProperties.addChangeCallback(new Runnable() { 111 @Override 112 public void run() { 113 syncATraceState(); 114 } 115 }); 116 } 117 118 private static void syncATraceState() { 119 long enabledFlags = SystemProperties.getLong("debug.atrace.tags.enableflags", 0); 120 TraceEvent.setATraceEnabled((enabledFlags & Trace.TRACE_TAG_WEBVIEW) != 0); 121 } 122 123 private void ensureChromiumStartedLocked(boolean onMainThread) { 124 assert Thread.holdsLock(mLock); 125 126 if (mStarted) { // Early-out for the common case. 127 return; 128 } 129 130 Looper looper = !onMainThread ? Looper.myLooper() : Looper.getMainLooper(); 131 Log.v(TAG, "Binding Chromium to " + 132 (Looper.getMainLooper().equals(looper) ? "main":"background") + 133 " looper " + looper); 134 ThreadUtils.setUiThread(looper); 135 136 if (ThreadUtils.runningOnUiThread()) { 137 startChromiumLocked(); 138 return; 139 } 140 141 // We must post to the UI thread to cover the case that the user has invoked Chromium 142 // startup by using the (thread-safe) CookieManager rather than creating a WebView. 143 ThreadUtils.postOnUiThread(new Runnable() { 144 @Override 145 public void run() { 146 synchronized (mLock) { 147 startChromiumLocked(); 148 } 149 } 150 }); 151 while (!mStarted) { 152 try { 153 // Important: wait() releases |mLock| the UI thread can take it :-) 154 mLock.wait(); 155 } catch (InterruptedException e) { 156 // Keep trying... eventually the UI thread will process the task we sent it. 157 } 158 } 159 } 160 161 private void startChromiumLocked() { 162 assert Thread.holdsLock(mLock) && ThreadUtils.runningOnUiThread(); 163 164 // The post-condition of this method is everything is ready, so notify now to cover all 165 // return paths. (Other threads will not wake-up until we release |mLock|, whatever). 166 mLock.notifyAll(); 167 168 if (mStarted) { 169 return; 170 } 171 172 if (Build.IS_DEBUGGABLE) { 173 CommandLine.initFromFile(COMMAND_LINE_FILE); 174 } else { 175 CommandLine.init(null); 176 } 177 178 CommandLine cl = CommandLine.getInstance(); 179 // TODO: currently in a relase build the DCHECKs only log. We either need to insall 180 // a report handler with SetLogReportHandler to make them assert, or else compile 181 // them out of the build altogether (b/8284203). Either way, so long they're 182 // compiled in, we may as unconditionally enable them here. 183 cl.appendSwitch("enable-dcheck"); 184 185 // TODO: Remove when GL is supported by default in the upstream code. 186 if (!cl.hasSwitch("disable-webview-gl-mode")) { 187 cl.appendSwitch("testing-webview-gl-mode"); 188 } 189 190 // We don't need to extract any paks because for WebView, they are 191 // in the system image. 192 ResourceExtractor.setMandatoryPaksToExtract(""); 193 194 try { 195 LibraryLoader.ensureInitialized(); 196 } catch(ProcessInitException e) { 197 throw new RuntimeException("Error initializing WebView library", e); 198 } 199 200 PathService.override(PathService.DIR_MODULE, "/system/lib/"); 201 // TODO: DIR_RESOURCE_PAKS_ANDROID needs to live somewhere sensible, 202 // inlined here for simplicity setting up the HTMLViewer demo. Unfortunately 203 // it can't go into base.PathService, as the native constant it refers to 204 // lives in the ui/ layer. See ui/base/ui_base_paths.h 205 final int DIR_RESOURCE_PAKS_ANDROID = 3003; 206 PathService.override(DIR_RESOURCE_PAKS_ANDROID, 207 "/system/framework/webview/paks"); 208 209 // Make sure that ResourceProvider is initialized before starting the browser process. 210 setUpResources(ActivityThread.currentApplication()); 211 AwBrowserProcess.start(ActivityThread.currentApplication()); 212 initPlatSupportLibrary(); 213 214 if (Build.IS_DEBUGGABLE) { 215 setWebContentsDebuggingEnabled(true); 216 } 217 218 initTraceEvent(); 219 mStarted = true; 220 221 for (WeakReference<WebViewChromium> wvc : mWebViewsToStart) { 222 WebViewChromium w = wvc.get(); 223 if (w != null) { 224 w.startYourEngine(); 225 } 226 } 227 mWebViewsToStart.clear(); 228 mWebViewsToStart = null; 229 230 // Start listening for data reduction proxy setting changes. 231 mProxyManager = new DataReductionProxyManager(); 232 mProxyManager.start(ActivityThread.currentApplication()); 233 } 234 235 boolean hasStarted() { 236 return mStarted; 237 } 238 239 void startYourEngines(boolean onMainThread) { 240 synchronized (mLock) { 241 ensureChromiumStartedLocked(onMainThread); 242 243 } 244 } 245 246 AwBrowserContext getBrowserContext() { 247 synchronized (mLock) { 248 return getBrowserContextLocked(); 249 } 250 } 251 252 private AwBrowserContext getBrowserContextLocked() { 253 assert Thread.holdsLock(mLock); 254 assert mStarted; 255 if (mBrowserContext == null) { 256 mBrowserContext = new AwBrowserContext( 257 ActivityThread.currentApplication().getSharedPreferences( 258 CHROMIUM_PREFS_NAME, Context.MODE_PRIVATE)); 259 } 260 return mBrowserContext; 261 } 262 263 private void setWebContentsDebuggingEnabled(boolean enable) { 264 if (Looper.myLooper() != ThreadUtils.getUiThreadLooper()) { 265 throw new RuntimeException( 266 "Toggling of Web Contents Debugging must be done on the UI thread"); 267 } 268 if (mDevToolsServer == null) { 269 if (!enable) return; 270 mDevToolsServer = new AwDevToolsServer(); 271 } 272 mDevToolsServer.setRemoteDebuggingEnabled(enable); 273 } 274 275 private void setUpResources(Context ctx) { 276 ResourceRewriter.rewriteRValues(ctx); 277 278 AwResource.setResources(ctx.getResources()); 279 AwResource.setErrorPageResources(android.R.raw.loaderror, 280 android.R.raw.nodomain); 281 AwResource.setDefaultTextEncoding( 282 android.R.string.config_webSettingsDefaultTextEncoding); 283 AwResource.setConfigKeySystemUuidMapping( 284 android.R.array.config_keySystemUuidMapping); 285 } 286 287 @Override 288 public Statics getStatics() { 289 synchronized (mLock) { 290 if (mStaticMethods == null) { 291 // TODO: Optimization potential: most these methods only need the native library 292 // loaded and initialized, not the entire browser process started. 293 // See also http://b/7009882 294 ensureChromiumStartedLocked(true); 295 mStaticMethods = new WebViewFactoryProvider.Statics() { 296 @Override 297 public String findAddress(String addr) { 298 return ContentViewStatics.findAddress(addr); 299 } 300 301 @Override 302 public String getDefaultUserAgent(Context context) { 303 return AwSettings.getDefaultUserAgent(); 304 } 305 306 @Override 307 public void setWebContentsDebuggingEnabled(boolean enable) { 308 // Web Contents debugging is always enabled on debug builds. 309 if (!Build.IS_DEBUGGABLE) { 310 WebViewChromiumFactoryProvider.this. 311 setWebContentsDebuggingEnabled(enable); 312 } 313 } 314 315 // TODO enable after L release to AOSP 316 //@Override 317 public void clearClientCertPreferences(Runnable onCleared) { 318 AwContentsStatics.clearClientCertPreferences(onCleared); 319 } 320 321 @Override 322 public void freeMemoryForTests() { 323 if (ActivityManager.isRunningInTestHarness()) { 324 MemoryPressureListener.maybeNotifyMemoryPresure( 325 ComponentCallbacks2.TRIM_MEMORY_COMPLETE); 326 } 327 } 328 329 @Override 330 public void optOutDataReductionProxy() { 331 DataReductionProxyManager.optOutDataReductionProxy(); 332 } 333 334 // TODO: Add @Override. 335 public void enableSlowWholeDocumentDraw() { 336 WebViewChromium.enableSlowWholeDocumentDraw(); 337 } 338 }; 339 } 340 } 341 return mStaticMethods; 342 } 343 344 @Override 345 public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) { 346 WebViewChromium wvc = new WebViewChromium(this, webView, privateAccess); 347 348 synchronized (mLock) { 349 if (mWebViewsToStart != null) { 350 mWebViewsToStart.add(new WeakReference<WebViewChromium>(wvc)); 351 } 352 } 353 354 return wvc; 355 } 356 357 @Override 358 public GeolocationPermissions getGeolocationPermissions() { 359 synchronized (mLock) { 360 if (mGeolocationPermissions == null) { 361 ensureChromiumStartedLocked(true); 362 mGeolocationPermissions = new GeolocationPermissionsAdapter( 363 getBrowserContextLocked().getGeolocationPermissions()); 364 } 365 } 366 return mGeolocationPermissions; 367 } 368 369 @Override 370 public CookieManager getCookieManager() { 371 synchronized (mLock) { 372 if (mCookieManager == null) { 373 if (!mStarted) { 374 // We can use CookieManager without starting Chromium; the native code 375 // will bring up just the parts it needs to make this work on a temporary 376 // basis until Chromium is started for real. The temporary cookie manager 377 // needs the application context to have been set. 378 ContentMain.initApplicationContext(ActivityThread.currentApplication()); 379 } 380 mCookieManager = new CookieManagerAdapter(new AwCookieManager()); 381 } 382 } 383 return mCookieManager; 384 } 385 386 @Override 387 public WebIconDatabase getWebIconDatabase() { 388 synchronized (mLock) { 389 if (mWebIconDatabase == null) { 390 ensureChromiumStartedLocked(true); 391 mWebIconDatabase = new WebIconDatabaseAdapter(); 392 } 393 } 394 return mWebIconDatabase; 395 } 396 397 @Override 398 public WebStorage getWebStorage() { 399 synchronized (mLock) { 400 if (mWebStorage == null) { 401 ensureChromiumStartedLocked(true); 402 mWebStorage = new WebStorageAdapter(AwQuotaManagerBridge.getInstance()); 403 } 404 } 405 return mWebStorage; 406 } 407 408 @Override 409 public WebViewDatabase getWebViewDatabase(Context context) { 410 synchronized (mLock) { 411 if (mWebViewDatabase == null) { 412 ensureChromiumStartedLocked(true); 413 AwBrowserContext browserContext = getBrowserContextLocked(); 414 mWebViewDatabase = new WebViewDatabaseAdapter( 415 browserContext.getFormDatabase(), 416 browserContext.getHttpAuthDatabase(context)); 417 } 418 } 419 return mWebViewDatabase; 420 } 421} 422