WebSettings.java revision ec696c0115bbc915417bd76af6432bc603b680d2
1/*
2 * Copyright (C) 2007 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 android.webkit;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.content.SharedPreferences;
22import android.os.Build;
23import android.os.Handler;
24import android.os.Message;
25import android.preference.PreferenceManager;
26import android.provider.Checkin;
27import android.provider.Settings;
28import android.util.Log;
29
30import java.io.File;
31import java.lang.SecurityException;
32
33import android.content.SharedPreferences.Editor;
34import android.content.pm.PackageManager;
35import android.database.sqlite.SQLiteDatabase;
36import android.database.sqlite.SQLiteException;
37import android.database.sqlite.SQLiteStatement;
38
39import java.util.HashSet;
40import java.util.Locale;
41
42/**
43 * Manages settings state for a WebView. When a WebView is first created, it
44 * obtains a set of default settings. These default settings will be returned
45 * from any getter call. A WebSettings object obtained from
46 * WebView.getSettings() is tied to the life of the WebView. If a WebView has
47 * been destroyed, any method call on WebSettings will throw an
48 * IllegalStateException.
49 */
50public class WebSettings {
51    /**
52     * Enum for controlling the layout of html.
53     * NORMAL means no rendering changes.
54     * SINGLE_COLUMN moves all content into one column that is the width of the
55     * view.
56     * NARROW_COLUMNS makes all columns no wider than the screen if possible.
57     */
58    // XXX: These must match LayoutAlgorithm in Settings.h in WebCore.
59    public enum LayoutAlgorithm {
60        NORMAL,
61        SINGLE_COLUMN,
62        NARROW_COLUMNS
63    }
64
65    /**
66     * Enum for specifying the text size.
67     * SMALLEST is 50%
68     * SMALLER is 75%
69     * NORMAL is 100%
70     * LARGER is 150%
71     * LARGEST is 200%
72     */
73    public enum TextSize {
74        SMALLEST(50),
75        SMALLER(75),
76        NORMAL(100),
77        LARGER(150),
78        LARGEST(200);
79        TextSize(int size) {
80            value = size;
81        }
82        int value;
83    }
84
85    /**
86     * Enum for specifying the WebView's desired density.
87     * FAR makes 100% looking like in 240dpi
88     * MEDIUM makes 100% looking like in 160dpi
89     * CLOSE makes 100% looking like in 120dpi
90     * @hide Pending API council approval
91     */
92    public enum ZoomDensity {
93        FAR(150),      // 240dpi
94        MEDIUM(100),    // 160dpi
95        CLOSE(75);     // 120dpi
96        ZoomDensity(int size) {
97            value = size;
98        }
99        int value;
100    }
101
102    /**
103     * Default cache usage pattern  Use with {@link #setCacheMode}.
104     */
105    public static final int LOAD_DEFAULT = -1;
106
107    /**
108     * Normal cache usage pattern  Use with {@link #setCacheMode}.
109     */
110    public static final int LOAD_NORMAL = 0;
111
112    /**
113     * Use cache if content is there, even if expired (eg, history nav)
114     * If it is not in the cache, load from network.
115     * Use with {@link #setCacheMode}.
116     */
117    public static final int LOAD_CACHE_ELSE_NETWORK = 1;
118
119    /**
120     * Don't use the cache, load from network
121     * Use with {@link #setCacheMode}.
122     */
123    public static final int LOAD_NO_CACHE = 2;
124
125    /**
126     * Don't use the network, load from cache only.
127     * Use with {@link #setCacheMode}.
128     */
129    public static final int LOAD_CACHE_ONLY = 3;
130
131    public enum RenderPriority {
132        NORMAL,
133        HIGH,
134        LOW
135    }
136
137    // WebView associated with this WebSettings.
138    private WebView mWebView;
139    // BrowserFrame used to access the native frame pointer.
140    private BrowserFrame mBrowserFrame;
141    // Flag to prevent multiple SYNC messages at one time.
142    private boolean mSyncPending = false;
143    // Custom handler that queues messages until the WebCore thread is active.
144    private final EventHandler mEventHandler;
145
146    // Private settings so we don't have to go into native code to
147    // retrieve the values. After setXXX, postSync() needs to be called.
148    //
149    // The default values need to match those in WebSettings.cpp
150    // If the defaults change, please also update the JavaDocs so developers
151    // know what they are.
152    private LayoutAlgorithm mLayoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS;
153    private Context         mContext;
154    private TextSize        mTextSize = TextSize.NORMAL;
155    private String          mStandardFontFamily = "sans-serif";
156    private String          mFixedFontFamily = "monospace";
157    private String          mSansSerifFontFamily = "sans-serif";
158    private String          mSerifFontFamily = "serif";
159    private String          mCursiveFontFamily = "cursive";
160    private String          mFantasyFontFamily = "fantasy";
161    private String          mDefaultTextEncoding;
162    private String          mUserAgent;
163    private boolean         mUseDefaultUserAgent;
164    private String          mAcceptLanguage;
165    private int             mMinimumFontSize = 8;
166    private int             mMinimumLogicalFontSize = 8;
167    private int             mDefaultFontSize = 16;
168    private int             mDefaultFixedFontSize = 13;
169    private boolean         mLoadsImagesAutomatically = true;
170    private boolean         mBlockNetworkImage = false;
171    private boolean         mBlockNetworkLoads;
172    private boolean         mJavaScriptEnabled = false;
173    private boolean         mPluginsEnabled = false;
174    private long            mWebStorageDefaultQuota = 0;
175    private boolean         mJavaScriptCanOpenWindowsAutomatically = false;
176    private boolean         mUseDoubleTree = false;
177    private boolean         mUseWideViewport = false;
178    private boolean         mSupportMultipleWindows = false;
179    private boolean         mShrinksStandaloneImagesToFit = false;
180    // Don't need to synchronize the get/set methods as they
181    // are basic types, also none of these values are used in
182    // native WebCore code.
183    private ZoomDensity     mDefaultZoom = ZoomDensity.MEDIUM;
184    private RenderPriority  mRenderPriority = RenderPriority.NORMAL;
185    private int             mOverrideCacheMode = LOAD_DEFAULT;
186    private boolean         mSaveFormData = true;
187    private boolean         mSavePassword = true;
188    private boolean         mLightTouchEnabled = false;
189    private boolean         mNeedInitialFocus = true;
190    private boolean         mNavDump = false;
191    private boolean         mSupportZoom = true;
192    private boolean         mBuiltInZoomControls = false;
193    private boolean         mAllowFileAccess = true;
194    private String          mDatabasePath = "";
195    private boolean         mDatabaseEnabled = false;
196    private String          mAppCachePath = "";
197    private boolean         mAppCacheEnabled = false;
198    private boolean         mDomStorageEnabled = false;
199
200    // Donut-specific hack to keep Gears permissions in sync with the
201    // system location setting.
202    // TODO: Make sure this hack is removed in  Eclair, when Gears
203    // is also removed.
204    // Used to remember if we checked the Gears permissions already.
205    static boolean mCheckedGearsPermissions = false;
206    // The Gears permissions database directory.
207    private final static String GEARS_DATABASE_DIR = "gears";
208    // The Gears permissions database file name.
209    private final static String GEARS_DATABASE_FILE = "permissions.db";
210    // The Gears location permissions table.
211    private final static String GEARS_LOCATION_ACCESS_TABLE_NAME =
212        "LocationAccess";
213    // The Gears storage access permissions table.
214    private final static String GEARS_STORAGE_ACCESS_TABLE_NAME = "Access";
215    // The Gears permissions db schema version table.
216    private final static String GEARS_SCHEMA_VERSION_TABLE_NAME =
217        "VersionInfo";
218    // The shared pref name.
219    private static final String LAST_KNOWN_LOCATION_SETTING =
220        "lastKnownLocationSystemSetting";
221    // The Browser package name.
222    private static final String BROWSER_PACKAGE_NAME = "com.android.browser";
223    // The Google URLs whitelisted for Gears location access.
224    private static HashSet<String> sGearsWhiteList;
225
226    static {
227        sGearsWhiteList = new HashSet<String>();
228        // NOTE: DO NOT ADD A "/" AT THE END!
229        sGearsWhiteList.add("http://www.google.com");
230        sGearsWhiteList.add("http://www.google.co.uk");
231    }
232
233    private static final String LOGTAG = "webcore";
234    static final boolean DEBUG = false;
235    static final boolean LOGV_ENABLED = DEBUG;
236
237    // Class to handle messages before WebCore is ready.
238    private class EventHandler {
239        // Message id for syncing
240        static final int SYNC = 0;
241        // Message id for setting priority
242        static final int PRIORITY = 1;
243        // Actual WebCore thread handler
244        private Handler mHandler;
245
246        private synchronized void createHandler() {
247            // as mRenderPriority can be set before thread is running, sync up
248            setRenderPriority();
249
250            // create a new handler
251            mHandler = new Handler() {
252                @Override
253                public void handleMessage(Message msg) {
254                    switch (msg.what) {
255                        case SYNC:
256                            synchronized (WebSettings.this) {
257                                checkGearsPermissions();
258                                if (mBrowserFrame.mNativeFrame != 0) {
259                                    nativeSync(mBrowserFrame.mNativeFrame);
260                                }
261                                mSyncPending = false;
262                            }
263                            break;
264
265                        case PRIORITY: {
266                            setRenderPriority();
267                            break;
268                        }
269                    }
270                }
271            };
272        }
273
274        private void setRenderPriority() {
275            synchronized (WebSettings.this) {
276                if (mRenderPriority == RenderPriority.NORMAL) {
277                    android.os.Process.setThreadPriority(
278                            android.os.Process.THREAD_PRIORITY_DEFAULT);
279                } else if (mRenderPriority == RenderPriority.HIGH) {
280                    android.os.Process.setThreadPriority(
281                            android.os.Process.THREAD_PRIORITY_FOREGROUND +
282                            android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
283                } else if (mRenderPriority == RenderPriority.LOW) {
284                    android.os.Process.setThreadPriority(
285                            android.os.Process.THREAD_PRIORITY_BACKGROUND);
286                }
287            }
288        }
289
290        /**
291         * Send a message to the private queue or handler.
292         */
293        private synchronized boolean sendMessage(Message msg) {
294            if (mHandler != null) {
295                mHandler.sendMessage(msg);
296                return true;
297            } else {
298                return false;
299            }
300        }
301    }
302
303    // User agent strings.
304    private static final String DESKTOP_USERAGENT =
305            "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
306            + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
307            + " Safari/530.17";
308    private static final String IPHONE_USERAGENT =
309            "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
310            + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
311            + " Mobile/7A341 Safari/528.16";
312    private static Locale sLocale;
313    private static Object sLockForLocaleSettings;
314
315    /**
316     * Package constructor to prevent clients from creating a new settings
317     * instance.
318     */
319    WebSettings(Context context, WebView webview) {
320        mEventHandler = new EventHandler();
321        mContext = context;
322        mWebView = webview;
323        mDefaultTextEncoding = context.getString(com.android.internal.
324                                                 R.string.default_text_encoding);
325
326        if (sLockForLocaleSettings == null) {
327            sLockForLocaleSettings = new Object();
328            sLocale = Locale.getDefault();
329        }
330        mAcceptLanguage = getCurrentAcceptLanguage();
331        mUserAgent = getCurrentUserAgent();
332        mUseDefaultUserAgent = true;
333
334        mBlockNetworkLoads = mContext.checkPermission(
335                "android.permission.INTERNET", android.os.Process.myPid(),
336                android.os.Process.myUid()) != PackageManager.PERMISSION_GRANTED;
337    }
338
339    /**
340     * Looks at sLocale and returns current AcceptLanguage String.
341     * @return Current AcceptLanguage String.
342     */
343    private String getCurrentAcceptLanguage() {
344        Locale locale;
345        synchronized(sLockForLocaleSettings) {
346            locale = sLocale;
347        }
348        StringBuffer buffer = new StringBuffer();
349        final String language = locale.getLanguage();
350        if (language != null) {
351            buffer.append(language);
352            final String country = locale.getCountry();
353            if (country != null) {
354                buffer.append("-");
355                buffer.append(country);
356            }
357        }
358        if (!locale.equals(Locale.US)) {
359            buffer.append(", ");
360            java.util.Locale us = Locale.US;
361            if (us.getLanguage() != null) {
362                buffer.append(us.getLanguage());
363                final String country = us.getCountry();
364                if (country != null) {
365                    buffer.append("-");
366                    buffer.append(country);
367                }
368            }
369        }
370
371        return buffer.toString();
372    }
373
374    /**
375     * Looks at sLocale and mContext and returns current UserAgent String.
376     * @return Current UserAgent String.
377     */
378    private synchronized String getCurrentUserAgent() {
379        Locale locale;
380        synchronized(sLockForLocaleSettings) {
381            locale = sLocale;
382        }
383        StringBuffer buffer = new StringBuffer();
384        // Add version
385        final String version = Build.VERSION.RELEASE;
386        if (version.length() > 0) {
387            buffer.append(version);
388        } else {
389            // default to "1.0"
390            buffer.append("1.0");
391        }
392        buffer.append("; ");
393        final String language = locale.getLanguage();
394        if (language != null) {
395            buffer.append(language.toLowerCase());
396            final String country = locale.getCountry();
397            if (country != null) {
398                buffer.append("-");
399                buffer.append(country.toLowerCase());
400            }
401        } else {
402            // default to "en"
403            buffer.append("en");
404        }
405
406        final String model = Build.MODEL;
407        if (model.length() > 0) {
408            buffer.append("; ");
409            buffer.append(model);
410        }
411        final String id = Build.ID;
412        if (id.length() > 0) {
413            buffer.append(" Build/");
414            buffer.append(id);
415        }
416        final String base = mContext.getResources().getText(
417                com.android.internal.R.string.web_user_agent).toString();
418        return String.format(base, buffer);
419    }
420
421    /**
422     * Enables dumping the pages navigation cache to a text file.
423     */
424    public void setNavDump(boolean enabled) {
425        mNavDump = enabled;
426    }
427
428    /**
429     * Returns true if dumping the navigation cache is enabled.
430     */
431    public boolean getNavDump() {
432        return mNavDump;
433    }
434
435    /**
436     * Set whether the WebView supports zoom
437     */
438    public void setSupportZoom(boolean support) {
439        mSupportZoom = support;
440    }
441
442    /**
443     * Returns whether the WebView supports zoom
444     */
445    public boolean supportZoom() {
446        return mSupportZoom;
447    }
448
449    /**
450     * Sets whether the zoom mechanism built into WebView is used.
451     */
452    public void setBuiltInZoomControls(boolean enabled) {
453        mBuiltInZoomControls = enabled;
454    }
455
456    /**
457     * Returns true if the zoom mechanism built into WebView is being used.
458     */
459    public boolean getBuiltInZoomControls() {
460        return mBuiltInZoomControls;
461    }
462
463    /**
464     * Enable or disable file access within WebView. File access is enabled by
465     * default.
466     */
467    public void setAllowFileAccess(boolean allow) {
468        mAllowFileAccess = allow;
469    }
470
471    /**
472     * Returns true if this WebView supports file access.
473     */
474    public boolean getAllowFileAccess() {
475        return mAllowFileAccess;
476    }
477
478    /**
479     * Store whether the WebView is saving form data.
480     */
481    public void setSaveFormData(boolean save) {
482        mSaveFormData = save;
483    }
484
485    /**
486     *  Return whether the WebView is saving form data.
487     */
488    public boolean getSaveFormData() {
489        return mSaveFormData;
490    }
491
492    /**
493     *  Store whether the WebView is saving password.
494     */
495    public void setSavePassword(boolean save) {
496        mSavePassword = save;
497    }
498
499    /**
500     *  Return whether the WebView is saving password.
501     */
502    public boolean getSavePassword() {
503        return mSavePassword;
504    }
505
506    /**
507     * Set the text size of the page.
508     * @param t A TextSize value for increasing or decreasing the text.
509     * @see WebSettings.TextSize
510     */
511    public synchronized void setTextSize(TextSize t) {
512        if (WebView.mLogEvent && mTextSize != t ) {
513            Checkin.updateStats(mContext.getContentResolver(),
514                    Checkin.Stats.Tag.BROWSER_TEXT_SIZE_CHANGE, 1, 0.0);
515        }
516        mTextSize = t;
517        postSync();
518    }
519
520    /**
521     * Get the text size of the page.
522     * @return A TextSize enum value describing the text size.
523     * @see WebSettings.TextSize
524     */
525    public synchronized TextSize getTextSize() {
526        return mTextSize;
527    }
528
529    /**
530     * Set the default zoom density of the page. This should be called from UI
531     * thread.
532     * @param zoom A ZoomDensity value
533     * @see WebSettings.ZoomDensity
534     * @hide Pending API council approval
535     */
536    public void setDefaultZoom(ZoomDensity zoom) {
537        if (mDefaultZoom != zoom) {
538            mDefaultZoom = zoom;
539            mWebView.updateDefaultZoomDensity(zoom.value);
540        }
541    }
542
543    /**
544     * Get the default zoom density of the page. This should be called from UI
545     * thread.
546     * @return A ZoomDensity value
547     * @see WebSettings.ZoomDensity
548     * @hide Pending API council approval
549     */
550    public ZoomDensity getDefaultZoom() {
551        return mDefaultZoom;
552    }
553
554    /**
555     * Enables using light touches to make a selection and activate mouseovers.
556     */
557    public void setLightTouchEnabled(boolean enabled) {
558        mLightTouchEnabled = enabled;
559    }
560
561    /**
562     * Returns true if light touches are enabled.
563     */
564    public boolean getLightTouchEnabled() {
565        return mLightTouchEnabled;
566    }
567
568    /**
569     * @deprecated This setting controlled a rendering optimization
570     * that is no longer present. Setting it now has no effect.
571     */
572    @Deprecated
573    public synchronized void setUseDoubleTree(boolean use) {
574        return;
575    }
576
577    /**
578     * @deprecated This setting controlled a rendering optimization
579     * that is no longer present. Setting it now has no effect.
580     */
581    @Deprecated
582    public synchronized boolean getUseDoubleTree() {
583        return false;
584    }
585
586    /**
587     * Tell the WebView about user-agent string.
588     * @param ua 0 if the WebView should use an Android user-agent string,
589     *           1 if the WebView should use a desktop user-agent string.
590     *
591     * @deprecated Please use setUserAgentString instead.
592     */
593    @Deprecated
594    public synchronized void setUserAgent(int ua) {
595        String uaString = null;
596        if (ua == 1) {
597            if (DESKTOP_USERAGENT.equals(mUserAgent)) {
598                return; // do nothing
599            } else {
600                uaString = DESKTOP_USERAGENT;
601            }
602        } else if (ua == 2) {
603            if (IPHONE_USERAGENT.equals(mUserAgent)) {
604                return; // do nothing
605            } else {
606                uaString = IPHONE_USERAGENT;
607            }
608        } else if (ua != 0) {
609            return; // do nothing
610        }
611        setUserAgentString(uaString);
612    }
613
614    /**
615     * Return user-agent as int
616     * @return int  0 if the WebView is using an Android user-agent string.
617     *              1 if the WebView is using a desktop user-agent string.
618     *             -1 if the WebView is using user defined user-agent string.
619     *
620     * @deprecated Please use getUserAgentString instead.
621     */
622    @Deprecated
623    public synchronized int getUserAgent() {
624        if (DESKTOP_USERAGENT.equals(mUserAgent)) {
625            return 1;
626        } else if (IPHONE_USERAGENT.equals(mUserAgent)) {
627            return 2;
628        } else if (mUseDefaultUserAgent) {
629            return 0;
630        }
631        return -1;
632    }
633
634    /**
635     * Tell the WebView to use the wide viewport
636     */
637    public synchronized void setUseWideViewPort(boolean use) {
638        if (mUseWideViewport != use) {
639            mUseWideViewport = use;
640            postSync();
641        }
642    }
643
644    /**
645     * @return True if the WebView is using a wide viewport
646     */
647    public synchronized boolean getUseWideViewPort() {
648        return mUseWideViewport;
649    }
650
651    /**
652     * Tell the WebView whether it supports multiple windows. TRUE means
653     *         that {@link WebChromeClient#onCreateWindow(WebView, boolean,
654     *         boolean, Message)} is implemented by the host application.
655     */
656    public synchronized void setSupportMultipleWindows(boolean support) {
657        if (mSupportMultipleWindows != support) {
658            mSupportMultipleWindows = support;
659            postSync();
660        }
661    }
662
663    /**
664     * @return True if the WebView is supporting multiple windows. This means
665     *         that {@link WebChromeClient#onCreateWindow(WebView, boolean,
666     *         boolean, Message)} is implemented by the host application.
667     */
668    public synchronized boolean supportMultipleWindows() {
669        return mSupportMultipleWindows;
670    }
671
672    /**
673     * Set the underlying layout algorithm. This will cause a relayout of the
674     * WebView.
675     * @param l A LayoutAlgorithm enum specifying the algorithm to use.
676     * @see WebSettings.LayoutAlgorithm
677     */
678    public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) {
679        // XXX: This will only be affective if libwebcore was built with
680        // ANDROID_LAYOUT defined.
681        if (mLayoutAlgorithm != l) {
682            mLayoutAlgorithm = l;
683            postSync();
684        }
685    }
686
687    /**
688     * Return the current layout algorithm. The default is NARROW_COLUMNS.
689     * @return LayoutAlgorithm enum value describing the layout algorithm
690     *         being used.
691     * @see WebSettings.LayoutAlgorithm
692     */
693    public synchronized LayoutAlgorithm getLayoutAlgorithm() {
694        return mLayoutAlgorithm;
695    }
696
697    /**
698     * Set the standard font family name.
699     * @param font A font family name.
700     */
701    public synchronized void setStandardFontFamily(String font) {
702        if (font != null && !font.equals(mStandardFontFamily)) {
703            mStandardFontFamily = font;
704            postSync();
705        }
706    }
707
708    /**
709     * Get the standard font family name. The default is "sans-serif".
710     * @return The standard font family name as a string.
711     */
712    public synchronized String getStandardFontFamily() {
713        return mStandardFontFamily;
714    }
715
716    /**
717     * Set the fixed font family name.
718     * @param font A font family name.
719     */
720    public synchronized void setFixedFontFamily(String font) {
721        if (font != null && !font.equals(mFixedFontFamily)) {
722            mFixedFontFamily = font;
723            postSync();
724        }
725    }
726
727    /**
728     * Get the fixed font family name. The default is "monospace".
729     * @return The fixed font family name as a string.
730     */
731    public synchronized String getFixedFontFamily() {
732        return mFixedFontFamily;
733    }
734
735    /**
736     * Set the sans-serif font family name.
737     * @param font A font family name.
738     */
739    public synchronized void setSansSerifFontFamily(String font) {
740        if (font != null && !font.equals(mSansSerifFontFamily)) {
741            mSansSerifFontFamily = font;
742            postSync();
743        }
744    }
745
746    /**
747     * Get the sans-serif font family name.
748     * @return The sans-serif font family name as a string.
749     */
750    public synchronized String getSansSerifFontFamily() {
751        return mSansSerifFontFamily;
752    }
753
754    /**
755     * Set the serif font family name. The default is "sans-serif".
756     * @param font A font family name.
757     */
758    public synchronized void setSerifFontFamily(String font) {
759        if (font != null && !font.equals(mSerifFontFamily)) {
760            mSerifFontFamily = font;
761            postSync();
762        }
763    }
764
765    /**
766     * Get the serif font family name. The default is "serif".
767     * @return The serif font family name as a string.
768     */
769    public synchronized String getSerifFontFamily() {
770        return mSerifFontFamily;
771    }
772
773    /**
774     * Set the cursive font family name.
775     * @param font A font family name.
776     */
777    public synchronized void setCursiveFontFamily(String font) {
778        if (font != null && !font.equals(mCursiveFontFamily)) {
779            mCursiveFontFamily = font;
780            postSync();
781        }
782    }
783
784    /**
785     * Get the cursive font family name. The default is "cursive".
786     * @return The cursive font family name as a string.
787     */
788    public synchronized String getCursiveFontFamily() {
789        return mCursiveFontFamily;
790    }
791
792    /**
793     * Set the fantasy font family name.
794     * @param font A font family name.
795     */
796    public synchronized void setFantasyFontFamily(String font) {
797        if (font != null && !font.equals(mFantasyFontFamily)) {
798            mFantasyFontFamily = font;
799            postSync();
800        }
801    }
802
803    /**
804     * Get the fantasy font family name. The default is "fantasy".
805     * @return The fantasy font family name as a string.
806     */
807    public synchronized String getFantasyFontFamily() {
808        return mFantasyFontFamily;
809    }
810
811    /**
812     * Set the minimum font size.
813     * @param size A non-negative integer between 1 and 72.
814     * Any number outside the specified range will be pinned.
815     */
816    public synchronized void setMinimumFontSize(int size) {
817        size = pin(size);
818        if (mMinimumFontSize != size) {
819            mMinimumFontSize = size;
820            postSync();
821        }
822    }
823
824    /**
825     * Get the minimum font size. The default is 8.
826     * @return A non-negative integer between 1 and 72.
827     */
828    public synchronized int getMinimumFontSize() {
829        return mMinimumFontSize;
830    }
831
832    /**
833     * Set the minimum logical font size.
834     * @param size A non-negative integer between 1 and 72.
835     * Any number outside the specified range will be pinned.
836     */
837    public synchronized void setMinimumLogicalFontSize(int size) {
838        size = pin(size);
839        if (mMinimumLogicalFontSize != size) {
840            mMinimumLogicalFontSize = size;
841            postSync();
842        }
843    }
844
845    /**
846     * Get the minimum logical font size. The default is 8.
847     * @return A non-negative integer between 1 and 72.
848     */
849    public synchronized int getMinimumLogicalFontSize() {
850        return mMinimumLogicalFontSize;
851    }
852
853    /**
854     * Set the default font size.
855     * @param size A non-negative integer between 1 and 72.
856     * Any number outside the specified range will be pinned.
857     */
858    public synchronized void setDefaultFontSize(int size) {
859        size = pin(size);
860        if (mDefaultFontSize != size) {
861            mDefaultFontSize = size;
862            postSync();
863        }
864    }
865
866    /**
867     * Get the default font size. The default is 16.
868     * @return A non-negative integer between 1 and 72.
869     */
870    public synchronized int getDefaultFontSize() {
871        return mDefaultFontSize;
872    }
873
874    /**
875     * Set the default fixed font size.
876     * @param size A non-negative integer between 1 and 72.
877     * Any number outside the specified range will be pinned.
878     */
879    public synchronized void setDefaultFixedFontSize(int size) {
880        size = pin(size);
881        if (mDefaultFixedFontSize != size) {
882            mDefaultFixedFontSize = size;
883            postSync();
884        }
885    }
886
887    /**
888     * Get the default fixed font size. The default is 16.
889     * @return A non-negative integer between 1 and 72.
890     */
891    public synchronized int getDefaultFixedFontSize() {
892        return mDefaultFixedFontSize;
893    }
894
895    /**
896     * Tell the WebView to load image resources automatically.
897     * @param flag True if the WebView should load images automatically.
898     */
899    public synchronized void setLoadsImagesAutomatically(boolean flag) {
900        if (mLoadsImagesAutomatically != flag) {
901            mLoadsImagesAutomatically = flag;
902            postSync();
903        }
904    }
905
906    /**
907     * Return true if the WebView will load image resources automatically.
908     * The default is true.
909     * @return True if the WebView loads images automatically.
910     */
911    public synchronized boolean getLoadsImagesAutomatically() {
912        return mLoadsImagesAutomatically;
913    }
914
915    /**
916     * Tell the WebView to block network image. This is only checked when
917     * getLoadsImagesAutomatically() is true.
918     * @param flag True if the WebView should block network image
919     */
920    public synchronized void setBlockNetworkImage(boolean flag) {
921        if (mBlockNetworkImage != flag) {
922            mBlockNetworkImage = flag;
923            postSync();
924        }
925    }
926
927    /**
928     * Return true if the WebView will block network image. The default is false.
929     * @return True if the WebView blocks network image.
930     */
931    public synchronized boolean getBlockNetworkImage() {
932        return mBlockNetworkImage;
933    }
934
935    /**
936     * @hide
937     * Tell the WebView to block all network load requests.
938     * @param flag True if the WebView should block all network loads
939     */
940    public synchronized void setBlockNetworkLoads(boolean flag) {
941        if (mBlockNetworkLoads != flag) {
942            mBlockNetworkLoads = flag;
943            verifyNetworkAccess();
944        }
945    }
946
947    /**
948     * @hide
949     * Return true if the WebView will block all network loads.
950     * The default is false.
951     * @return True if the WebView blocks all network loads.
952     */
953    public synchronized boolean getBlockNetworkLoads() {
954        return mBlockNetworkLoads;
955    }
956
957
958    private void verifyNetworkAccess() {
959        if (!mBlockNetworkLoads) {
960            if (mContext.checkPermission("android.permission.INTERNET",
961                    android.os.Process.myPid(), android.os.Process.myUid()) !=
962                        PackageManager.PERMISSION_GRANTED) {
963                throw new SecurityException
964                        ("Permission denied - " +
965                                "application missing INTERNET permission");
966            }
967        }
968    }
969
970    /**
971     * Tell the WebView to enable javascript execution.
972     * @param flag True if the WebView should execute javascript.
973     */
974    public synchronized void setJavaScriptEnabled(boolean flag) {
975        if (mJavaScriptEnabled != flag) {
976            mJavaScriptEnabled = flag;
977            postSync();
978        }
979    }
980
981    /**
982     * Tell the WebView to enable plugins.
983     * @param flag True if the WebView should load plugins.
984     */
985    public synchronized void setPluginsEnabled(boolean flag) {
986        if (mPluginsEnabled != flag) {
987            mPluginsEnabled = flag;
988            postSync();
989        }
990    }
991
992    /**
993     * TODO: need to add @Deprecated
994     */
995    public synchronized void setPluginsPath(String pluginsPath) {
996    }
997
998    /**
999     * @hide
1000     * Set the default quota for WebStorage DBs
1001     * @param quota the default quota in bytes
1002     */
1003    public synchronized void setWebStorageDefaultQuota(long quota) {
1004        if (mWebStorageDefaultQuota != quota) {
1005            mWebStorageDefaultQuota = quota;
1006            postSync();
1007        }
1008    }
1009
1010    /**
1011     * Set the path to where database storage API databases should be saved.
1012     * This will update WebCore when the Sync runs in the C++ side.
1013     * @param databasePath String path to the directory where databases should
1014     *     be saved. May be the empty string but should never be null.
1015     */
1016    public synchronized void setDatabasePath(String databasePath) {
1017        if (databasePath != null && !databasePath.equals(mDatabasePath)) {
1018            mDatabasePath = databasePath;
1019            postSync();
1020        }
1021    }
1022
1023    /**
1024     * Tell the WebView to enable Application Caches API.
1025     * @param flag True if the WebView should enable Application Caches.
1026     * @hide pending api council approval
1027     */
1028    public synchronized void setAppCacheEnabled(boolean flag) {
1029        if (mAppCacheEnabled != flag) {
1030            mAppCacheEnabled = flag;
1031            postSync();
1032        }
1033    }
1034
1035    /**
1036     * Set a custom path to the Application Caches files. The client
1037     * must ensure it exists before this call.
1038     * @param appCachePath String path to the directory containing Application
1039     * Caches files. The appCache path can be the empty string but should not
1040     * be null. Passing null for this parameter will result in a no-op.
1041     * @hide pending api council approval
1042     */
1043    public synchronized void setAppCachePath(String appCachePath) {
1044        if (appCachePath != null && !appCachePath.equals(mAppCachePath)) {
1045            mAppCachePath = appCachePath;
1046            postSync();
1047        }
1048    }
1049
1050    /**
1051     * Set whether the database storage API is enabled.
1052     * @param flag boolean True if the WebView should use the database storage
1053     *     API.
1054     */
1055    public synchronized void setDatabaseEnabled(boolean flag) {
1056       if (mDatabaseEnabled != flag) {
1057           mDatabaseEnabled = flag;
1058           postSync();
1059       }
1060    }
1061
1062    /**
1063     * Set whether the DOM storage API is enabled.
1064     * @param flag boolean True if the WebView should use the DOM storage
1065     *     API.
1066     * @hide pending API council.
1067     */
1068    public synchronized void setDomStorageEnabled(boolean flag) {
1069       if (mDomStorageEnabled != flag) {
1070           mDomStorageEnabled = flag;
1071           postSync();
1072       }
1073    }
1074
1075    /**
1076     * Returns true if the DOM Storage API's are enabled.
1077     * @return True if the DOM Storage API's are enabled.
1078     * @hide pending API council.
1079     */
1080    public synchronized boolean getDomStorageEnabled() {
1081       return mDomStorageEnabled;
1082    }
1083
1084    /**
1085     * Return the path to where database storage API databases are saved for
1086     * the current WebView.
1087     * @return the String path to the database storage API databases.
1088     */
1089    public synchronized String getDatabasePath() {
1090        return mDatabasePath;
1091    }
1092
1093    /**
1094     * Returns true if database storage API is enabled.
1095     * @return True if the database storage API is enabled.
1096     */
1097    public synchronized boolean getDatabaseEnabled() {
1098        return mDatabaseEnabled;
1099    }
1100
1101    /**
1102     * Return true if javascript is enabled. <b>Note: The default is false.</b>
1103     * @return True if javascript is enabled.
1104     */
1105    public synchronized boolean getJavaScriptEnabled() {
1106        return mJavaScriptEnabled;
1107    }
1108
1109    /**
1110     * Return true if plugins are enabled.
1111     * @return True if plugins are enabled.
1112     */
1113    public synchronized boolean getPluginsEnabled() {
1114        return mPluginsEnabled;
1115    }
1116
1117    /**
1118     * TODO: need to add @Deprecated
1119     */
1120    public synchronized String getPluginsPath() {
1121        return "";
1122    }
1123
1124    /**
1125     * @hide
1126     * Return the default quota for WebStorage DBs
1127     * @return the default quota in bytes
1128     */
1129    public synchronized long getWebStorageDefaultQuota() {
1130        return mWebStorageDefaultQuota;
1131    }
1132
1133    /**
1134     * Tell javascript to open windows automatically. This applies to the
1135     * javascript function window.open().
1136     * @param flag True if javascript can open windows automatically.
1137     */
1138    public synchronized void setJavaScriptCanOpenWindowsAutomatically(
1139            boolean flag) {
1140        if (mJavaScriptCanOpenWindowsAutomatically != flag) {
1141            mJavaScriptCanOpenWindowsAutomatically = flag;
1142            postSync();
1143        }
1144    }
1145
1146    /**
1147     * Return true if javascript can open windows automatically. The default
1148     * is false.
1149     * @return True if javascript can open windows automatically during
1150     *         window.open().
1151     */
1152    public synchronized boolean getJavaScriptCanOpenWindowsAutomatically() {
1153        return mJavaScriptCanOpenWindowsAutomatically;
1154    }
1155
1156    /**
1157     * Set the default text encoding name to use when decoding html pages.
1158     * @param encoding The text encoding name.
1159     */
1160    public synchronized void setDefaultTextEncodingName(String encoding) {
1161        if (encoding != null && !encoding.equals(mDefaultTextEncoding)) {
1162            mDefaultTextEncoding = encoding;
1163            postSync();
1164        }
1165    }
1166
1167    /**
1168     * Get the default text encoding name. The default is "Latin-1".
1169     * @return The default text encoding name as a string.
1170     */
1171    public synchronized String getDefaultTextEncodingName() {
1172        return mDefaultTextEncoding;
1173    }
1174
1175    /**
1176     * Set the WebView's user-agent string. If the string "ua" is null or empty,
1177     * it will use the system default user-agent string.
1178     */
1179    public synchronized void setUserAgentString(String ua) {
1180        if (ua == null || ua.length() == 0) {
1181            synchronized(sLockForLocaleSettings) {
1182                Locale currentLocale = Locale.getDefault();
1183                if (!sLocale.equals(currentLocale)) {
1184                    sLocale = currentLocale;
1185                    mAcceptLanguage = getCurrentAcceptLanguage();
1186                }
1187            }
1188            ua = getCurrentUserAgent();
1189            mUseDefaultUserAgent = true;
1190        } else  {
1191            mUseDefaultUserAgent = false;
1192        }
1193
1194        if (!ua.equals(mUserAgent)) {
1195            mUserAgent = ua;
1196            postSync();
1197        }
1198    }
1199
1200    /**
1201     * Return the WebView's user-agent string.
1202     */
1203    public synchronized String getUserAgentString() {
1204        if (DESKTOP_USERAGENT.equals(mUserAgent) ||
1205                IPHONE_USERAGENT.equals(mUserAgent) ||
1206                !mUseDefaultUserAgent) {
1207            return mUserAgent;
1208        }
1209
1210        boolean doPostSync = false;
1211        synchronized(sLockForLocaleSettings) {
1212            Locale currentLocale = Locale.getDefault();
1213            if (!sLocale.equals(currentLocale)) {
1214                sLocale = currentLocale;
1215                mUserAgent = getCurrentUserAgent();
1216                mAcceptLanguage = getCurrentAcceptLanguage();
1217                doPostSync = true;
1218            }
1219        }
1220        if (doPostSync) {
1221            postSync();
1222        }
1223        return mUserAgent;
1224    }
1225
1226    /* package api to grab the Accept Language string. */
1227    /*package*/ synchronized String getAcceptLanguage() {
1228        synchronized(sLockForLocaleSettings) {
1229            Locale currentLocale = Locale.getDefault();
1230            if (!sLocale.equals(currentLocale)) {
1231                sLocale = currentLocale;
1232                mAcceptLanguage = getCurrentAcceptLanguage();
1233            }
1234        }
1235        return mAcceptLanguage;
1236    }
1237
1238    /**
1239     * Tell the WebView whether it needs to set a node to have focus when
1240     * {@link WebView#requestFocus(int, android.graphics.Rect)} is called.
1241     *
1242     * @param flag
1243     */
1244    public void setNeedInitialFocus(boolean flag) {
1245        if (mNeedInitialFocus != flag) {
1246            mNeedInitialFocus = flag;
1247        }
1248    }
1249
1250    /* Package api to get the choice whether it needs to set initial focus. */
1251    /* package */ boolean getNeedInitialFocus() {
1252        return mNeedInitialFocus;
1253    }
1254
1255    /**
1256     * Set the priority of the Render thread. Unlike the other settings, this
1257     * one only needs to be called once per process. The default is NORMAL.
1258     *
1259     * @param priority RenderPriority, can be normal, high or low.
1260     */
1261    public synchronized void setRenderPriority(RenderPriority priority) {
1262        if (mRenderPriority != priority) {
1263            mRenderPriority = priority;
1264            mEventHandler.sendMessage(Message.obtain(null,
1265                    EventHandler.PRIORITY));
1266        }
1267    }
1268
1269    /**
1270     * Override the way the cache is used. The way the cache is used is based
1271     * on the navigation option. For a normal page load, the cache is checked
1272     * and content is re-validated as needed. When navigating back, content is
1273     * not revalidated, instead the content is just pulled from the cache.
1274     * This function allows the client to override this behavior.
1275     * @param mode One of the LOAD_ values.
1276     */
1277    public void setCacheMode(int mode) {
1278        if (mode != mOverrideCacheMode) {
1279            mOverrideCacheMode = mode;
1280        }
1281    }
1282
1283    /**
1284     * Return the current setting for overriding the cache mode. For a full
1285     * description, see the {@link #setCacheMode(int)} function.
1286     */
1287    public int getCacheMode() {
1288        return mOverrideCacheMode;
1289    }
1290
1291    /**
1292     * If set, webkit alternately shrinks and expands images viewed outside
1293     * of an HTML page to fit the screen. This conflicts with attempts by
1294     * the UI to zoom in and out of an image, so it is set false by default.
1295     * @param shrink Set true to let webkit shrink the standalone image to fit.
1296     * {@hide}
1297     */
1298    public void setShrinksStandaloneImagesToFit(boolean shrink) {
1299        if (mShrinksStandaloneImagesToFit != shrink) {
1300            mShrinksStandaloneImagesToFit = shrink;
1301            postSync();
1302        }
1303     }
1304
1305    /**
1306     * Transfer messages from the queue to the new WebCoreThread. Called from
1307     * WebCore thread.
1308     */
1309    /*package*/
1310    synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) {
1311        mBrowserFrame = frame;
1312        if (DebugFlags.WEB_SETTINGS) {
1313            junit.framework.Assert.assertTrue(frame.mNativeFrame != 0);
1314        }
1315        nativeSync(frame.mNativeFrame);
1316        mSyncPending = false;
1317        mEventHandler.createHandler();
1318    }
1319
1320    private int pin(int size) {
1321        // FIXME: 72 is just an arbitrary max text size value.
1322        if (size < 1) {
1323            return 1;
1324        } else if (size > 72) {
1325            return 72;
1326        }
1327        return size;
1328    }
1329
1330    private void checkGearsPermissions() {
1331        // Did we already check the permissions?
1332        if (mCheckedGearsPermissions) {
1333            return;
1334        }
1335        // Are we running in the browser?
1336        if (!BROWSER_PACKAGE_NAME.equals(mContext.getPackageName())) {
1337            return;
1338        }
1339
1340        // Remember we checked the Gears permissions.
1341        mCheckedGearsPermissions = true;
1342        // Get the current system settings.
1343        int setting = Settings.Secure.getInt(mContext.getContentResolver(),
1344                Settings.Secure.USE_LOCATION_FOR_SERVICES, -1);
1345        // Check if we need to set the Gears permissions.
1346        if (setting != -1 && locationSystemSettingChanged(setting)) {
1347            setGearsPermissionForGoogleDomains(setting);
1348        }
1349    }
1350
1351    private boolean locationSystemSettingChanged(int newSetting) {
1352        SharedPreferences prefs =
1353            PreferenceManager.getDefaultSharedPreferences(mContext);
1354        int oldSetting = 0;
1355        oldSetting = prefs.getInt(LAST_KNOWN_LOCATION_SETTING, oldSetting);
1356        if (oldSetting == newSetting) {
1357            return false;
1358        }
1359        Editor ed = prefs.edit();
1360        ed.putInt(LAST_KNOWN_LOCATION_SETTING, newSetting);
1361        ed.commit();
1362        return true;
1363    }
1364
1365    private void setGearsPermissionForGoogleDomains(int systemPermission) {
1366        // Transform the system permission into a Gears permission
1367        int gearsPermission = (systemPermission == 1 ? 1 : 2);
1368        // Build the path to the Gears library.
1369
1370        // hack for now
1371        File file = mContext.getDir("plugins", 0);
1372        if (file == null) {
1373            return;
1374        }
1375        file = file.getParentFile();
1376        // Build the Gears database file name.
1377        file = new File(file.getAbsolutePath() + File.separator
1378                + GEARS_DATABASE_DIR + File.separator + GEARS_DATABASE_FILE);
1379        // Remember whether or not we need to create the LocationAccess table.
1380        boolean needToCreateTables = !file.exists();
1381        // Try opening the Gears database.
1382        SQLiteDatabase permissions;
1383        try {
1384            permissions = SQLiteDatabase.openOrCreateDatabase(file, null);
1385        } catch (SQLiteException e) {
1386            if (LOGV_ENABLED) {
1387                Log.v(LOGTAG, "Could not open Gears permission DB: " +
1388                        e.getMessage());
1389            }
1390            // Just bail out.
1391            return;
1392        }
1393        // We now have a database open. Begin a transaction.
1394        permissions.beginTransaction();
1395        try {
1396            if (needToCreateTables) {
1397                // Create the tables. Note that this creates the
1398                // Gears tables for the permissions DB schema version 2.
1399                // The Gears schema upgrade process will take care of the rest.
1400                // First, the storage access table.
1401                SQLiteStatement statement = permissions.compileStatement(
1402                        "CREATE TABLE IF NOT EXISTS " +
1403                        GEARS_STORAGE_ACCESS_TABLE_NAME +
1404                        " (Name TEXT UNIQUE, Value)");
1405                statement.execute();
1406                // Next the location access table.
1407                statement = permissions.compileStatement(
1408                        "CREATE TABLE IF NOT EXISTS " +
1409                        GEARS_LOCATION_ACCESS_TABLE_NAME +
1410                        " (Name TEXT UNIQUE, Value)");
1411                statement.execute();
1412                // Finally, the schema version table.
1413                statement = permissions.compileStatement(
1414                        "CREATE TABLE IF NOT EXISTS " +
1415                        GEARS_SCHEMA_VERSION_TABLE_NAME +
1416                        " (Name TEXT UNIQUE, Value)");
1417                statement.execute();
1418                // Set the schema version to 2.
1419                ContentValues schema = new ContentValues();
1420                schema.put("Name", "Version");
1421                schema.put("Value", 2);
1422                permissions.insert(GEARS_SCHEMA_VERSION_TABLE_NAME, null,
1423                        schema);
1424            }
1425
1426            ContentValues permissionValues = new ContentValues();
1427
1428            for (String url : sGearsWhiteList) {
1429                permissionValues.put("Name", url);
1430                permissionValues.put("Value", gearsPermission);
1431                permissions.replace(GEARS_LOCATION_ACCESS_TABLE_NAME, null,
1432                        permissionValues);
1433                permissionValues.clear();
1434            }
1435            // Commit the transaction.
1436            permissions.setTransactionSuccessful();
1437        } catch (SQLiteException e) {
1438            if (LOGV_ENABLED) {
1439                Log.v(LOGTAG, "Could not set the Gears permissions: " +
1440                        e.getMessage());
1441            }
1442        } finally {
1443            permissions.endTransaction();
1444            permissions.close();
1445        }
1446    }
1447    /* Post a SYNC message to handle syncing the native settings. */
1448    private synchronized void postSync() {
1449        // Only post if a sync is not pending
1450        if (!mSyncPending) {
1451            mSyncPending = mEventHandler.sendMessage(
1452                    Message.obtain(null, EventHandler.SYNC));
1453        }
1454    }
1455
1456    // Synchronize the native and java settings.
1457    private native void nativeSync(int nativeFrame);
1458}
1459