1 // Copyright (c) 2012 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 org.xwalk.core;
7 import android.content.Context;
8 import android.content.pm.PackageManager;
9 import android.os.Handler;
10 import android.os.Looper;
11 import android.os.Message;
12 import android.os.Process;
13 import android.webkit.WebSettings;
15 import org.chromium.base.CalledByNative;
16 import org.chromium.base.JNINamespace;
17 import org.chromium.base.ThreadUtils;
19 @JNINamespace("xwalk")
20 public class XWalkSettings {
22 private static final String TAG = "XWalkSettings";
24 // This class must be created on the UI thread. Afterwards, it can be
25 // used from any thread. Internally, the class uses a message queue
26 // to call native code on the UI thread only.
28 // Lock to protect all settings.
29 private final Object mXWalkSettingsLock = new Object();
31 private final Context mContext;
33 private boolean mAllowScriptsToCloseWindows = true;
34 private boolean mLoadsImagesAutomatically = true;
35 private boolean mImagesEnabled = true;
36 private boolean mJavaScriptEnabled = true;
37 private boolean mAllowUniversalAccessFromFileURLs = false;
38 private boolean mAllowFileAccessFromFileURLs = false;
39 private boolean mJavaScriptCanOpenWindowsAutomatically = true;
40 private int mCacheMode = WebSettings.LOAD_DEFAULT;
41 private boolean mSupportMultipleWindows = false;
42 private boolean mAppCacheEnabled = true;
43 private boolean mDomStorageEnabled = true;
44 private boolean mDatabaseEnabled = true;
45 private boolean mUseWideViewport = false;
46 private boolean mMediaPlaybackRequiresUserGesture = false;
47 private String mDefaultVideoPosterURL;
49 // Not accessed by the native side.
50 private boolean mBlockNetworkLoads; // Default depends on permission of embedding APK.
51 private boolean mAllowContentUrlAccess = true;
52 private boolean mAllowFileUrlAccess = true;
53 private boolean mShouldFocusFirstNode = true;
54 private boolean mGeolocationEnabled = true;
55 private String mUserAgent;
57 // Protects access to settings global fields.
58 private static final Object sGlobalContentSettingsLock = new Object();
59 // For compatibility with the legacy WebView, we can only enable AppCache when the path is
60 // provided. However, we don't use the path, so we just check if we have received it from the
62 private static boolean sAppCachePathIsSet = false;
64 // The native side of this object.
65 private int mNativeXWalkSettings = 0;
67 // A flag to avoid sending superfluous synchronization messages.
68 private boolean mIsUpdateWebkitPrefsMessagePending = false;
69 // Custom handler that queues messages to call native code on the UI thread.
70 private final EventHandler mEventHandler;
72 private static final int MINIMUM_FONT_SIZE = 1;
73 private static final int MAXIMUM_FONT_SIZE = 72;
75 static class LazyDefaultUserAgent{
76 private static final String sInstance = nativeGetDefaultUserAgent();
79 // Class to handle messages to be processed on the UI thread.
80 private class EventHandler {
81 // Message id for updating Webkit preferences
82 private static final int UPDATE_WEBKIT_PREFERENCES = 0;
83 // Actual UI thread handler
84 private Handler mHandler;
90 if (mHandler != null) return;
91 mHandler = new Handler(ThreadUtils.getUiThreadLooper()) {
93 public void handleMessage(Message msg) {
95 case UPDATE_WEBKIT_PREFERENCES:
96 synchronized (mXWalkSettingsLock) {
97 updateWebkitPreferencesOnUiThread();
98 mIsUpdateWebkitPrefsMessagePending = false;
99 mXWalkSettingsLock.notifyAll();
107 void maybeRunOnUiThreadBlocking(Runnable r) {
108 if (mHandler != null) {
109 ThreadUtils.runOnUiThreadBlocking(r);
113 private void updateWebkitPreferencesLocked() {
114 assert Thread.holdsLock(mXWalkSettingsLock);
115 if (mNativeXWalkSettings == 0) return;
116 if (mHandler == null) return;
117 if (ThreadUtils.runningOnUiThread()) {
118 updateWebkitPreferencesOnUiThread();
120 // We're being called on a background thread, so post a message.
121 if (mIsUpdateWebkitPrefsMessagePending) {
124 mIsUpdateWebkitPrefsMessagePending = true;
125 mHandler.sendMessage(Message.obtain(null, UPDATE_WEBKIT_PREFERENCES));
126 // We must block until the settings have been sync'd to native to
127 // ensure that they have taken effect.
129 while (mIsUpdateWebkitPrefsMessagePending) {
130 mXWalkSettingsLock.wait();
132 } catch (InterruptedException e) {}
137 public XWalkSettings(Context context, int nativeWebContents,
138 boolean isAccessFromFileURLsGrantedByDefault) {
139 ThreadUtils.assertOnUiThread();
141 mBlockNetworkLoads = mContext.checkPermission(
142 android.Manifest.permission.INTERNET,
144 Process.myUid()) != PackageManager.PERMISSION_GRANTED;
146 if (isAccessFromFileURLsGrantedByDefault) {
147 mAllowUniversalAccessFromFileURLs = true;
148 mAllowFileAccessFromFileURLs = true;
151 mUserAgent = LazyDefaultUserAgent.sInstance;
153 mEventHandler = new EventHandler();
155 setWebContents(nativeWebContents);
158 void setWebContents(int nativeWebContents) {
159 synchronized (mXWalkSettingsLock) {
160 if (mNativeXWalkSettings != 0) {
161 nativeDestroy(mNativeXWalkSettings);
162 assert mNativeXWalkSettings == 0;
164 if (nativeWebContents != 0) {
165 mEventHandler.bindUiThread();
166 mNativeXWalkSettings = nativeInit(nativeWebContents);
167 nativeUpdateEverythingLocked(mNativeXWalkSettings);
173 private void nativeXWalkSettingsGone(int nativeXWalkSettings) {
174 assert mNativeXWalkSettings != 0 && mNativeXWalkSettings == nativeXWalkSettings;
175 mNativeXWalkSettings = 0;
178 public void setAllowScriptsToCloseWindows(boolean allow) {
179 synchronized (mXWalkSettingsLock) {
180 if (mAllowScriptsToCloseWindows != allow) {
181 mAllowScriptsToCloseWindows = allow;
186 public boolean getAllowScriptsToCloseWindows() {
187 synchronized (mXWalkSettingsLock) {
188 return mAllowScriptsToCloseWindows;
193 * See {@link android.webkit.WebSettings#setCacheMode}.
195 public void setCacheMode(int mode) {
196 synchronized (mXWalkSettingsLock) {
197 if (mCacheMode != mode) {
204 * See {@link android.webkit.WebSettings#getCacheMode}.
206 public int getCacheMode() {
207 synchronized (mXWalkSettingsLock) {
213 * See {@link android.webkit.WebSettings#setBlockNetworkLoads}.
215 public void setBlockNetworkLoads(boolean flag) {
216 synchronized (mXWalkSettingsLock) {
217 if (!flag && mContext.checkPermission(
218 android.Manifest.permission.INTERNET,
220 Process.myUid()) != PackageManager.PERMISSION_GRANTED) {
221 throw new SecurityException("Permission denied - " +
222 "application missing INTERNET permission");
224 mBlockNetworkLoads = flag;
229 * See {@link android.webkit.WebSettings#getBlockNetworkLoads}.
231 public boolean getBlockNetworkLoads() {
232 synchronized (mXWalkSettingsLock) {
233 return mBlockNetworkLoads;
238 * See {@link android.webkit.WebSettings#setAllowFileAccess}.
240 public void setAllowFileAccess(boolean allow) {
241 synchronized (mXWalkSettingsLock) {
242 if (mAllowFileUrlAccess != allow) {
243 mAllowFileUrlAccess = allow;
249 * See {@link android.webkit.WebSettings#getAllowFileAccess}.
251 public boolean getAllowFileAccess() {
252 synchronized (mXWalkSettingsLock) {
253 return mAllowFileUrlAccess;
258 * See {@link android.webkit.WebSettings#setAllowContentAccess}.
260 public void setAllowContentAccess(boolean allow) {
261 synchronized (mXWalkSettingsLock) {
262 if (mAllowContentUrlAccess != allow) {
263 mAllowContentUrlAccess = allow;
269 * See {@link android.webkit.WebSettings#getAllowContentAccess}.
271 public boolean getAllowContentAccess() {
272 synchronized (mXWalkSettingsLock) {
273 return mAllowContentUrlAccess;
278 * See {@link android.webkit.WebSettings#setGeolocationEnabled}.
280 public void setGeolocationEnabled(boolean flag) {
281 synchronized (mXWalkSettingsLock) {
282 if (mGeolocationEnabled != flag) {
283 mGeolocationEnabled = flag;
289 * @return Returns if geolocation is currently enabled.
291 boolean getGeolocationEnabled() {
292 synchronized (mXWalkSettingsLock) {
293 return mGeolocationEnabled;
298 * See {@link android.webkit.WebSettings#setJavaScriptEnabled}.
300 public void setJavaScriptEnabled(boolean flag) {
301 synchronized (mXWalkSettingsLock) {
302 if (mJavaScriptEnabled != flag) {
303 mJavaScriptEnabled = flag;
304 mEventHandler.updateWebkitPreferencesLocked();
310 * See {@link android.webkit.WebSettings#setAllowUniversalAccessFromFileURLs}.
312 public void setAllowUniversalAccessFromFileURLs(boolean flag) {
313 synchronized (mXWalkSettingsLock) {
314 if (mAllowUniversalAccessFromFileURLs != flag) {
315 mAllowUniversalAccessFromFileURLs = flag;
316 mEventHandler.updateWebkitPreferencesLocked();
322 * See {@link android.webkit.WebSettings#setAllowFileAccessFromFileURLs}.
324 public void setAllowFileAccessFromFileURLs(boolean flag) {
325 synchronized (mXWalkSettingsLock) {
326 if (mAllowFileAccessFromFileURLs != flag) {
327 mAllowFileAccessFromFileURLs = flag;
328 mEventHandler.updateWebkitPreferencesLocked();
334 * See {@link android.webkit.WebSettings#setLoadsImagesAutomatically}.
336 public void setLoadsImagesAutomatically(boolean flag) {
337 synchronized (mXWalkSettingsLock) {
338 if (mLoadsImagesAutomatically != flag) {
339 mLoadsImagesAutomatically = flag;
340 mEventHandler.updateWebkitPreferencesLocked();
346 * See {@link android.webkit.WebSettings#getLoadsImagesAutomatically}.
348 public boolean getLoadsImagesAutomatically() {
349 synchronized (mXWalkSettingsLock) {
350 return mLoadsImagesAutomatically;
355 * See {@link android.webkit.WebSettings#setImagesEnabled}.
357 public void setImagesEnabled(boolean flag) {
358 synchronized (mXWalkSettingsLock) {
359 if (mImagesEnabled != flag) {
360 mImagesEnabled = flag;
361 mEventHandler.updateWebkitPreferencesLocked();
367 * See {@link android.webkit.WebSettings#getImagesEnabled}.
369 public boolean getImagesEnabled() {
370 synchronized (mXWalkSettingsLock) {
371 return mImagesEnabled;
376 * See {@link android.webkit.WebSettings#getJavaScriptEnabled}.
378 public boolean getJavaScriptEnabled() {
379 synchronized (mXWalkSettingsLock) {
380 return mJavaScriptEnabled;
385 * See {@link android.webkit.WebSettings#getAllowUniversalAccessFromFileURLs}.
387 public boolean getAllowUniversalAccessFromFileURLs() {
388 synchronized (mXWalkSettingsLock) {
389 return mAllowUniversalAccessFromFileURLs;
394 * See {@link android.webkit.WebSettings#getAllowFileAccessFromFileURLs}.
396 public boolean getAllowFileAccessFromFileURLs() {
397 synchronized (mXWalkSettingsLock) {
398 return mAllowFileAccessFromFileURLs;
403 * See {@link android.webkit.WebSettings#setJavaScriptCanOpenWindowsAutomatically}.
405 public void setJavaScriptCanOpenWindowsAutomatically(boolean flag) {
406 synchronized (mXWalkSettingsLock) {
407 if (mJavaScriptCanOpenWindowsAutomatically != flag) {
408 mJavaScriptCanOpenWindowsAutomatically = flag;
409 mEventHandler.updateWebkitPreferencesLocked();
415 * See {@link android.webkit.WebSettings#getJavaScriptCanOpenWindowsAutomatically}.
417 public boolean getJavaScriptCanOpenWindowsAutomatically() {
418 synchronized (mXWalkSettingsLock) {
419 return mJavaScriptCanOpenWindowsAutomatically;
424 * See {@link android.webkit.WebSettings#setSupportMultipleWindows}.
426 public void setSupportMultipleWindows(boolean support) {
427 synchronized (mXWalkSettingsLock) {
428 if (mSupportMultipleWindows != support) {
429 mSupportMultipleWindows = support;
430 mEventHandler.updateWebkitPreferencesLocked();
436 * See {@link android.webkit.WebSettings#supportMultipleWindows}.
438 public boolean supportMultipleWindows() {
439 synchronized (mXWalkSettingsLock) {
440 return mSupportMultipleWindows;
445 * See {@link android.webkit.WebSettings#setUseWideViewPort}.
447 public void setUseWideViewPort(boolean use) {
448 synchronized (mXWalkSettingsLock) {
449 if (mUseWideViewport != use) {
450 mUseWideViewport = use;
451 mEventHandler.updateWebkitPreferencesLocked();
457 * See {@link android.webkit.WebSettings#getUseWideViewPort}.
459 public boolean getUseWideViewPort() {
460 synchronized (mXWalkSettingsLock) {
461 return mUseWideViewport;
466 * See {@link android.webkit.WebSettings#setAppCacheEnabled}.
468 public void setAppCacheEnabled(boolean flag) {
469 synchronized (mXWalkSettingsLock) {
470 if (mAppCacheEnabled != flag) {
471 mAppCacheEnabled = flag;
472 mEventHandler.updateWebkitPreferencesLocked();
478 * See {@link android.webkit.WebSettings#setAppCachePath}.
480 public void setAppCachePath(String path) {
481 boolean needToSync = false;
482 synchronized (sGlobalContentSettingsLock) {
483 // AppCachePath can only be set once.
484 if (!sAppCachePathIsSet && path != null && !path.isEmpty()) {
485 sAppCachePathIsSet = true;
489 // The obvious problem here is that other WebViews will not be updated,
490 // until they execute synchronization from Java to the native side.
491 // But this is the same behaviour as it was in the legacy WebView.
493 synchronized (mXWalkSettingsLock) {
494 mEventHandler.updateWebkitPreferencesLocked();
500 * Gets whether Application Cache is enabled.
502 * @return true if Application Cache is enabled
506 private boolean getAppCacheEnabled() {
507 // This should only be called from UpdateWebkitPreferences, which is called
508 // either from the constructor, or with mXWalkSettingsLock being held.
509 if (!mAppCacheEnabled) {
512 synchronized (sGlobalContentSettingsLock) {
513 return sAppCachePathIsSet;
518 * See {@link android.webkit.WebSettings#setDomStorageEnabled}.
520 public void setDomStorageEnabled(boolean flag) {
521 synchronized (mXWalkSettingsLock) {
522 if (mDomStorageEnabled != flag) {
523 mDomStorageEnabled = flag;
524 mEventHandler.updateWebkitPreferencesLocked();
530 * See {@link android.webkit.WebSettings#getDomStorageEnabled}.
532 public boolean getDomStorageEnabled() {
533 synchronized (mXWalkSettingsLock) {
534 return mDomStorageEnabled;
539 * See {@link android.webkit.WebSettings#setDatabaseEnabled}.
541 public void setDatabaseEnabled(boolean flag) {
542 synchronized (mXWalkSettingsLock) {
543 if (mDatabaseEnabled != flag) {
544 mDatabaseEnabled = flag;
545 mEventHandler.updateWebkitPreferencesLocked();
551 * See {@link android.webkit.WebSettings#getDatabaseEnabled}.
553 public boolean getDatabaseEnabled() {
554 synchronized (mXWalkSettingsLock) {
555 return mDatabaseEnabled;
560 * See {@link android.webkit.WebSettings#setMediaPlaybackRequiresUserGesture}.
562 public void setMediaPlaybackRequiresUserGesture(boolean require) {
563 synchronized (mXWalkSettingsLock) {
564 if (mMediaPlaybackRequiresUserGesture != require) {
565 mMediaPlaybackRequiresUserGesture = require;
566 mEventHandler.updateWebkitPreferencesLocked();
572 * See {@link android.webkit.WebSettings#getMediaPlaybackRequiresUserGesture}.
574 public boolean getMediaPlaybackRequiresUserGesture() {
575 synchronized (mXWalkSettingsLock) {
576 return mMediaPlaybackRequiresUserGesture;
581 * See {@link android.webkit.WebSettings#setDefaultVideoPosterURL}.
583 public void setDefaultVideoPosterURL(String url) {
584 synchronized (mXWalkSettingsLock) {
585 if (mDefaultVideoPosterURL != null && !mDefaultVideoPosterURL.equals(url) ||
586 mDefaultVideoPosterURL == null && url != null) {
587 mDefaultVideoPosterURL = url;
588 mEventHandler.updateWebkitPreferencesLocked();
594 * @returns the default User-Agent used by each ContentViewCore instance, i.e. unless
595 * overridden by {@link #setUserAgentString()}
597 public static String getDefaultUserAgent() {
598 return LazyDefaultUserAgent.sInstance;
602 * See {@link android.webkit.WebSettings#setUserAgentString}.
604 public void setUserAgentString(String ua) {
605 synchronized (mXWalkSettingsLock) {
606 final String oldUserAgent = mUserAgent;
607 if (ua == null || ua.length() == 0) {
608 mUserAgent = LazyDefaultUserAgent.sInstance;
612 if (!oldUserAgent.equals(mUserAgent)) {
613 mEventHandler.maybeRunOnUiThreadBlocking(new Runnable() {
616 if (mNativeXWalkSettings != 0) {
617 nativeUpdateUserAgent(mNativeXWalkSettings);
626 * See {@link android.webkit.WebSettings#getUserAgentString}.
628 public String getUserAgentString() {
629 synchronized (mXWalkSettingsLock) {
635 private String getUserAgentLocked() {
640 * See {@link android.webkit.WebSettings#getDefaultVideoPosterURL}.
642 public String getDefaultVideoPosterURL() {
643 synchronized (mXWalkSettingsLock) {
644 return mDefaultVideoPosterURL;
649 private void updateEverything() {
650 synchronized (mXWalkSettingsLock) {
651 nativeUpdateEverythingLocked(mNativeXWalkSettings);
655 private void updateWebkitPreferencesOnUiThread() {
656 if (mNativeXWalkSettings != 0) {
657 ThreadUtils.assertOnUiThread();
658 nativeUpdateWebkitPreferences(mNativeXWalkSettings);
662 private native int nativeInit(int webContentsPtr);
664 private native void nativeDestroy(int nativeXWalkSettings);
666 private static native String nativeGetDefaultUserAgent();
668 private native void nativeUpdateEverythingLocked(int nativeXWalkSettings);
670 private native void nativeUpdateUserAgent(int nativeXWalkSettings);
672 private native void nativeUpdateWebkitPreferences(int nativeXWalkSettings);