WebViewChromiumFactoryProvider.java revision 2de7a52b3eeaa7b77570d8c0ce01b80ba38a754a
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.app.ActivityThread; 20import android.content.Context; 21import android.content.SharedPreferences; 22import android.os.Build; 23import android.os.Looper; 24import android.util.Log; 25import android.webkit.CookieManager; 26import android.webkit.GeolocationPermissions; 27import android.webkit.WebIconDatabase; 28import android.webkit.WebStorage; 29import android.webkit.WebView; 30import android.webkit.WebViewDatabase; 31import android.webkit.WebViewFactoryProvider; 32import android.webkit.WebViewProvider; 33 34import org.chromium.android_webview.AwBrowserContext; 35import org.chromium.android_webview.AwBrowserProcess; 36import org.chromium.android_webview.AwContents; 37import org.chromium.android_webview.AwCookieManager; 38import org.chromium.android_webview.AwDevToolsServer; 39import org.chromium.android_webview.AwFormDatabase; 40import org.chromium.android_webview.AwGeolocationPermissions; 41import org.chromium.android_webview.AwQuotaManagerBridge; 42import org.chromium.android_webview.AwSettings; 43import org.chromium.base.CommandLine; 44import org.chromium.base.PathService; 45import org.chromium.base.ThreadUtils; 46import org.chromium.content.app.ContentMain; 47import org.chromium.content.app.LibraryLoader; 48import org.chromium.content.browser.ContentViewStatics; 49import org.chromium.content.browser.ResourceExtractor; 50import org.chromium.content.common.ProcessInitException; 51 52import java.lang.ref.WeakReference; 53import java.util.ArrayList; 54 55public class WebViewChromiumFactoryProvider implements WebViewFactoryProvider { 56 57 private final String TAG = "WebViewChromiumFactoryProvider"; 58 59 private static final String CHROMIUM_PREFS_NAME = "WebViewChromiumPrefs"; 60 private static final String COMMAND_LINE_FILE = "/data/local/tmp/webview-command-line"; 61 62 // Guards accees to the other members, and is notifyAll() signalled on the UI thread 63 // when the chromium process has been started. 64 private final Object mLock = new Object(); 65 66 // Initialization guarded by mLock. 67 private AwBrowserContext mBrowserContext; 68 private Statics mStaticMethods; 69 private GeolocationPermissionsAdapter mGeolocationPermissions; 70 private CookieManagerAdapter mCookieManager; 71 private WebIconDatabaseAdapter mWebIconDatabase; 72 private WebStorageAdapter mWebStorage; 73 private WebViewDatabaseAdapter mWebViewDatabase; 74 private AwDevToolsServer mDevToolsServer; 75 76 private ArrayList<WeakReference<WebViewChromium>> mWebViewsToStart = 77 new ArrayList<WeakReference<WebViewChromium>>(); 78 79 // Read/write protected by mLock. 80 private boolean mStarted; 81 82 public WebViewChromiumFactoryProvider() { 83 // Load chromium library. 84 AwBrowserProcess.loadLibrary(); 85 // Load glue-layer support library. 86 System.loadLibrary("webviewchromium_plat_support"); 87 ThreadUtils.setWillOverrideUiThread(); 88 } 89 90 private void initPlatSupportLibrary() { 91 DrawGLFunctor.setChromiumAwDrawGLFunction(AwContents.getAwDrawGLFunction()); 92 AwContents.setAwDrawSWFunctionTable(GraphicsUtils.getDrawSWFunctionTable()); 93 AwContents.setAwDrawGLFunctionTable(GraphicsUtils.getDrawGLFunctionTable()); 94 } 95 96 private void ensureChromiumStartedLocked(boolean onMainThread) { 97 assert Thread.holdsLock(mLock); 98 99 if (mStarted) { // Early-out for the common case. 100 return; 101 } 102 103 Looper looper = !onMainThread ? Looper.myLooper() : Looper.getMainLooper(); 104 Log.v(TAG, "Binding Chromium to " + 105 (Looper.getMainLooper().equals(looper) ? "main":"background") + 106 " looper " + looper); 107 ThreadUtils.setUiThread(looper); 108 109 if (ThreadUtils.runningOnUiThread()) { 110 startChromiumLocked(); 111 return; 112 } 113 114 // We must post to the UI thread to cover the case that the user has invoked Chromium 115 // startup by using the (thread-safe) CookieManager rather than creating a WebView. 116 ThreadUtils.postOnUiThread(new Runnable() { 117 @Override 118 public void run() { 119 synchronized (mLock) { 120 startChromiumLocked(); 121 } 122 } 123 }); 124 while (!mStarted) { 125 try { 126 // Important: wait() releases |mLock| so the UI thread can take it :-) 127 mLock.wait(); 128 } catch (InterruptedException e) { 129 // Keep trying... eventually the UI thread will process the task we sent it. 130 } 131 } 132 } 133 134 private void startChromiumLocked() { 135 assert Thread.holdsLock(mLock) && ThreadUtils.runningOnUiThread(); 136 137 // The post-condition of this method is everything is ready, so notify now to cover all 138 // return paths. (Other threads will not wake-up until we release |mLock|, whatever). 139 mLock.notifyAll(); 140 141 if (mStarted) { 142 return; 143 } 144 145 if (Build.IS_DEBUGGABLE) { 146 CommandLine.initFromFile(COMMAND_LINE_FILE); 147 } else { 148 CommandLine.init(null); 149 } 150 151 CommandLine cl = CommandLine.getInstance(); 152 // TODO: currently in a relase build the DCHECKs only log. We either need to insall 153 // a report handler with SetLogReportHandler to make them assert, or else compile 154 // them out of the build altogether (b/8284203). Either way, so long they're 155 // compiled in, we may as unconditionally enable them here. 156 cl.appendSwitch("enable-dcheck"); 157 158 // TODO: Remove when GL is supported by default in the upstream code. 159 if (!cl.hasSwitch("disable-webview-gl-mode")) { 160 cl.appendSwitch("testing-webview-gl-mode"); 161 } 162 163 Context context = ActivityThread.currentApplication(); 164 if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) { 165 cl.appendSwitch("enable-webview-classic-workarounds"); 166 } 167 168 // We don't need to extract any paks because for WebView, they are 169 // in the system image. 170 ResourceExtractor.setMandatoryPaksToExtract(""); 171 172 try { 173 LibraryLoader.ensureInitialized(); 174 } catch(ProcessInitException e) { 175 throw new RuntimeException("Error initializing WebView library", e); 176 } 177 178 PathService.override(PathService.DIR_MODULE, "/system/lib/"); 179 // TODO: DIR_RESOURCE_PAKS_ANDROID needs to live somewhere sensible, 180 // inlined here for simplicity setting up the HTMLViewer demo. Unfortunately 181 // it can't go into base.PathService, as the native constant it refers to 182 // lives in the ui/ layer. See ui/base/ui_base_paths.h 183 final int DIR_RESOURCE_PAKS_ANDROID = 3003; 184 PathService.override(DIR_RESOURCE_PAKS_ANDROID, 185 "/system/framework/webview/paks"); 186 187 AwBrowserProcess.start(ActivityThread.currentApplication()); 188 initPlatSupportLibrary(); 189 190 if (Build.IS_DEBUGGABLE) { 191 setWebContentsDebuggingEnabled(true); 192 } 193 mStarted = true; 194 195 for (WeakReference<WebViewChromium> wvc : mWebViewsToStart) { 196 WebViewChromium w = wvc.get(); 197 if (w != null) { 198 w.startYourEngine(); 199 } 200 } 201 mWebViewsToStart.clear(); 202 mWebViewsToStart = null; 203 } 204 205 boolean hasStarted() { 206 return mStarted; 207 } 208 209 void startYourEngines(boolean onMainThread) { 210 synchronized (mLock) { 211 ensureChromiumStartedLocked(onMainThread); 212 213 } 214 } 215 216 AwBrowserContext getBrowserContext() { 217 synchronized (mLock) { 218 return getBrowserContextLocked(); 219 } 220 } 221 222 private AwBrowserContext getBrowserContextLocked() { 223 assert Thread.holdsLock(mLock); 224 assert mStarted; 225 if (mBrowserContext == null) { 226 mBrowserContext = new AwBrowserContext( 227 ActivityThread.currentApplication().getSharedPreferences( 228 CHROMIUM_PREFS_NAME, Context.MODE_PRIVATE)); 229 } 230 return mBrowserContext; 231 } 232 233 private void setWebContentsDebuggingEnabled(boolean enable) { 234 if (Looper.myLooper() != ThreadUtils.getUiThreadLooper()) { 235 throw new RuntimeException( 236 "Toggling of Web Contents Debugging must be done on the UI thread"); 237 } 238 if (mDevToolsServer == null) { 239 if (!enable) return; 240 mDevToolsServer = new AwDevToolsServer(); 241 } 242 mDevToolsServer.setRemoteDebuggingEnabled(enable); 243 } 244 245 246 @Override 247 public Statics getStatics() { 248 synchronized (mLock) { 249 if (mStaticMethods == null) { 250 // TODO: Optimization potential: most these methods only need the native library 251 // loaded and initialized, not the entire browser process started. 252 // See also http://b/7009882 253 ensureChromiumStartedLocked(true); 254 mStaticMethods = new WebViewFactoryProvider.Statics() { 255 @Override 256 public String findAddress(String addr) { 257 return ContentViewStatics.findAddress(addr); 258 } 259 260 @Override 261 public void setPlatformNotificationsEnabled(boolean enable) { 262 // noop 263 } 264 265 @Override 266 public String getDefaultUserAgent(Context context) { 267 return AwSettings.getDefaultUserAgent(); 268 } 269 270 @Override 271 public void setWebContentsDebuggingEnabled(boolean enable) { 272 // Web Contents debugging is always enabled on debug builds. 273 if (!Build.IS_DEBUGGABLE) { 274 WebViewChromiumFactoryProvider.this. 275 setWebContentsDebuggingEnabled(enable); 276 } 277 } 278 }; 279 } 280 } 281 return mStaticMethods; 282 } 283 284 @Override 285 public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) { 286 WebViewChromium wvc = new WebViewChromium(this, webView, privateAccess); 287 288 synchronized (mLock) { 289 if (mWebViewsToStart != null) { 290 mWebViewsToStart.add(new WeakReference<WebViewChromium>(wvc)); 291 } 292 } 293 ResourceProvider.registerResources(webView.getContext()); 294 return wvc; 295 } 296 297 @Override 298 public GeolocationPermissions getGeolocationPermissions() { 299 synchronized (mLock) { 300 if (mGeolocationPermissions == null) { 301 ensureChromiumStartedLocked(true); 302 mGeolocationPermissions = new GeolocationPermissionsAdapter( 303 getBrowserContextLocked().getGeolocationPermissions()); 304 } 305 } 306 return mGeolocationPermissions; 307 } 308 309 @Override 310 public CookieManager getCookieManager() { 311 synchronized (mLock) { 312 if (mCookieManager == null) { 313 if (!mStarted) { 314 // We can use CookieManager without starting Chromium; the native code 315 // will bring up just the parts it needs to make this work on a temporary 316 // basis until Chromium is started for real. The temporary cookie manager 317 // needs the application context to have been set. 318 ContentMain.initApplicationContext(ActivityThread.currentApplication()); 319 } 320 mCookieManager = new CookieManagerAdapter(new AwCookieManager()); 321 } 322 } 323 return mCookieManager; 324 } 325 326 @Override 327 public WebIconDatabase getWebIconDatabase() { 328 synchronized (mLock) { 329 if (mWebIconDatabase == null) { 330 ensureChromiumStartedLocked(true); 331 mWebIconDatabase = new WebIconDatabaseAdapter(); 332 } 333 } 334 return mWebIconDatabase; 335 } 336 337 @Override 338 public WebStorage getWebStorage() { 339 synchronized (mLock) { 340 if (mWebStorage == null) { 341 ensureChromiumStartedLocked(true); 342 mWebStorage = new WebStorageAdapter(AwQuotaManagerBridge.getInstance()); 343 } 344 } 345 return mWebStorage; 346 } 347 348 @Override 349 public WebViewDatabase getWebViewDatabase(Context context) { 350 synchronized (mLock) { 351 if (mWebViewDatabase == null) { 352 ensureChromiumStartedLocked(true); 353 AwBrowserContext browserContext = getBrowserContextLocked(); 354 mWebViewDatabase = new WebViewDatabaseAdapter( 355 browserContext.getFormDatabase(), 356 browserContext.getHttpAuthDatabase(context)); 357 } 358 } 359 return mWebViewDatabase; 360 } 361} 362