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