BrowserSettings.java revision 5b6a8eb74fa151139e39ecea196439432b43a4e1
1
2/*
3 * Copyright (C) 2007 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.browser;
19
20import com.android.browser.search.SearchEngine;
21import com.android.browser.search.SearchEngines;
22
23import android.app.ActivityManager;
24import android.content.ComponentName;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.pm.ActivityInfo;
28import android.content.SharedPreferences;
29import android.content.SharedPreferences.Editor;
30import android.database.ContentObserver;
31import android.os.Handler;
32import android.preference.PreferenceActivity;
33import android.preference.PreferenceScreen;
34import android.provider.Settings;
35import android.util.Log;
36import android.webkit.CookieManager;
37import android.webkit.GeolocationPermissions;
38import android.webkit.ValueCallback;
39import android.webkit.WebView;
40import android.webkit.WebViewDatabase;
41import android.webkit.WebIconDatabase;
42import android.webkit.WebSettings;
43import android.webkit.WebStorage;
44import android.preference.PreferenceManager;
45import android.provider.Browser;
46
47import java.util.HashMap;
48import java.util.Map;
49import java.util.Set;
50import java.util.Observable;
51
52/*
53 * Package level class for storing various WebView and Browser settings. To use
54 * this class:
55 * BrowserSettings s = BrowserSettings.getInstance();
56 * s.addObserver(webView.getSettings());
57 * s.loadFromDb(context); // Only needed on app startup
58 * s.javaScriptEnabled = true;
59 * ... // set any other settings
60 * s.update(); // this will update all the observers
61 *
62 * To remove an observer:
63 * s.deleteObserver(webView.getSettings());
64 */
65class BrowserSettings extends Observable {
66
67    // Private variables for settings
68    // NOTE: these defaults need to be kept in sync with the XML
69    // until the performance of PreferenceManager.setDefaultValues()
70    // is improved.
71    // Note: boolean variables are set inside reset function.
72    private boolean loadsImagesAutomatically;
73    private boolean javaScriptEnabled;
74    private WebSettings.PluginState pluginState;
75    private boolean javaScriptCanOpenWindowsAutomatically;
76    private boolean showSecurityWarnings;
77    private boolean rememberPasswords;
78    private boolean saveFormData;
79    private boolean openInBackground;
80    private String defaultTextEncodingName;
81    private String homeUrl = "";
82    private SearchEngine searchEngine;
83    private boolean autoFitPage;
84    private boolean landscapeOnly;
85    private boolean loadsPageInOverviewMode;
86    private boolean showDebugSettings;
87    // HTML5 API flags
88    private boolean appCacheEnabled;
89    private boolean databaseEnabled;
90    private boolean domStorageEnabled;
91    private boolean geolocationEnabled;
92    private boolean workersEnabled;  // only affects V8. JSC does not have a similar setting
93    // HTML5 API configuration params
94    private long appCacheMaxSize = Long.MAX_VALUE;
95    private String appCachePath;  // default value set in loadFromDb().
96    private String databasePath; // default value set in loadFromDb()
97    private String geolocationDatabasePath; // default value set in loadFromDb()
98    private WebStorageSizeManager webStorageSizeManager;
99
100    private String jsFlags = "";
101
102    private final static String TAG = "BrowserSettings";
103
104    // Development settings
105    public WebSettings.LayoutAlgorithm layoutAlgorithm =
106        WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
107    private boolean useWideViewPort = true;
108    private int userAgent = 0;
109    private boolean tracing = false;
110    private boolean lightTouch = false;
111    private boolean navDump = false;
112
113    // By default the error console is shown once the user navigates to about:debug.
114    // The setting can be then toggled from the settings menu.
115    private boolean showConsole = true;
116
117    // Private preconfigured values
118    private static int minimumFontSize = 8;
119    private static int minimumLogicalFontSize = 8;
120    private static int defaultFontSize = 16;
121    private static int defaultFixedFontSize = 13;
122    private static WebSettings.TextSize textSize =
123        WebSettings.TextSize.NORMAL;
124    private static WebSettings.ZoomDensity zoomDensity =
125        WebSettings.ZoomDensity.MEDIUM;
126    private static int pageCacheCapacity;
127
128    // Preference keys that are used outside this class
129    public final static String PREF_CLEAR_CACHE = "privacy_clear_cache";
130    public final static String PREF_CLEAR_COOKIES = "privacy_clear_cookies";
131    public final static String PREF_CLEAR_HISTORY = "privacy_clear_history";
132    public final static String PREF_HOMEPAGE = "homepage";
133    public final static String PREF_SEARCH_ENGINE = "search_engine";
134    public final static String PREF_CLEAR_FORM_DATA =
135            "privacy_clear_form_data";
136    public final static String PREF_CLEAR_PASSWORDS =
137            "privacy_clear_passwords";
138    public final static String PREF_EXTRAS_RESET_DEFAULTS =
139            "reset_default_preferences";
140    public final static String PREF_DEBUG_SETTINGS = "debug_menu";
141    public final static String PREF_WEBSITE_SETTINGS = "website_settings";
142    public final static String PREF_TEXT_SIZE = "text_size";
143    public final static String PREF_DEFAULT_ZOOM = "default_zoom";
144    public final static String PREF_DEFAULT_TEXT_ENCODING =
145            "default_text_encoding";
146    public final static String PREF_CLEAR_GEOLOCATION_ACCESS =
147            "privacy_clear_geolocation_access";
148
149    private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (Macintosh; " +
150            "U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.17 (KHTML, " +
151            "like Gecko) Version/4.0 Safari/530.17";
152
153    private static final String IPHONE_USERAGENT = "Mozilla/5.0 (iPhone; U; " +
154            "CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 " +
155            "(KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16";
156
157    // Value to truncate strings when adding them to a TextView within
158    // a ListView
159    public final static int MAX_TEXTVIEW_LEN = 80;
160
161    private TabControl mTabControl;
162
163    // Single instance of the BrowserSettings for use in the Browser app.
164    private static BrowserSettings sSingleton;
165
166    // Private map of WebSettings to Observer objects used when deleting an
167    // observer.
168    private HashMap<WebSettings,Observer> mWebSettingsToObservers =
169        new HashMap<WebSettings,Observer>();
170
171    /*
172     * An observer wrapper for updating a WebSettings object with the new
173     * settings after a call to BrowserSettings.update().
174     */
175    static class Observer implements java.util.Observer {
176        // Private WebSettings object that will be updated.
177        private WebSettings mSettings;
178
179        Observer(WebSettings w) {
180            mSettings = w;
181        }
182
183        public void update(Observable o, Object arg) {
184            BrowserSettings b = (BrowserSettings)o;
185            WebSettings s = mSettings;
186
187            s.setLayoutAlgorithm(b.layoutAlgorithm);
188            if (b.userAgent == 0) {
189                // use the default ua string
190                s.setUserAgentString(null);
191            } else if (b.userAgent == 1) {
192                s.setUserAgentString(DESKTOP_USERAGENT);
193            } else if (b.userAgent == 2) {
194                s.setUserAgentString(IPHONE_USERAGENT);
195            }
196            s.setUseWideViewPort(b.useWideViewPort);
197            s.setLoadsImagesAutomatically(b.loadsImagesAutomatically);
198            s.setJavaScriptEnabled(b.javaScriptEnabled);
199            s.setPluginState(b.pluginState);
200            s.setJavaScriptCanOpenWindowsAutomatically(
201                    b.javaScriptCanOpenWindowsAutomatically);
202            s.setDefaultTextEncodingName(b.defaultTextEncodingName);
203            s.setMinimumFontSize(b.minimumFontSize);
204            s.setMinimumLogicalFontSize(b.minimumLogicalFontSize);
205            s.setDefaultFontSize(b.defaultFontSize);
206            s.setDefaultFixedFontSize(b.defaultFixedFontSize);
207            s.setNavDump(b.navDump);
208            s.setTextSize(b.textSize);
209            s.setDefaultZoom(b.zoomDensity);
210            s.setLightTouchEnabled(b.lightTouch);
211            s.setSaveFormData(b.saveFormData);
212            s.setSavePassword(b.rememberPasswords);
213            s.setLoadWithOverviewMode(b.loadsPageInOverviewMode);
214            s.setPageCacheCapacity(pageCacheCapacity);
215
216            // WebView inside Browser doesn't want initial focus to be set.
217            s.setNeedInitialFocus(false);
218            // Browser supports multiple windows
219            s.setSupportMultipleWindows(true);
220
221            // HTML5 API flags
222            s.setAppCacheEnabled(b.appCacheEnabled);
223            s.setDatabaseEnabled(b.databaseEnabled);
224            s.setDomStorageEnabled(b.domStorageEnabled);
225            s.setWorkersEnabled(b.workersEnabled);  // This only affects V8.
226            s.setGeolocationEnabled(b.geolocationEnabled);
227
228            // HTML5 configuration parameters.
229            s.setAppCacheMaxSize(b.appCacheMaxSize);
230            s.setAppCachePath(b.appCachePath);
231            s.setDatabasePath(b.databasePath);
232            s.setGeolocationDatabasePath(b.geolocationDatabasePath);
233
234            b.updateTabControlSettings();
235        }
236    }
237
238    /**
239     * Load settings from the browser app's database.
240     * NOTE: Strings used for the preferences must match those specified
241     * in the browser_preferences.xml
242     * @param ctx A Context object used to query the browser's settings
243     *            database. If the database exists, the saved settings will be
244     *            stored in this BrowserSettings object. This will update all
245     *            observers of this object.
246     */
247    public void loadFromDb(final Context ctx) {
248        SharedPreferences p =
249                PreferenceManager.getDefaultSharedPreferences(ctx);
250        // Set the default value for the Application Caches path.
251        appCachePath = ctx.getDir("appcache", 0).getPath();
252        // Determine the maximum size of the application cache.
253        webStorageSizeManager = new WebStorageSizeManager(
254                ctx,
255                new WebStorageSizeManager.StatFsDiskInfo(appCachePath),
256                new WebStorageSizeManager.WebKitAppCacheInfo(appCachePath));
257        appCacheMaxSize = webStorageSizeManager.getAppCacheMaxSize();
258        // Set the default value for the Database path.
259        databasePath = ctx.getDir("databases", 0).getPath();
260        // Set the default value for the Geolocation database path.
261        geolocationDatabasePath = ctx.getDir("geolocation", 0).getPath();
262
263        if (p.getString(PREF_HOMEPAGE, "") == "") {
264            // No home page preferences is set, set it to default.
265            setHomePage(ctx, getFactoryResetHomeUrl(ctx));
266        }
267
268        // the cost of one cached page is ~3M (measured using nytimes.com). For
269        // low end devices, we only cache one page. For high end devices, we try
270        // to cache more pages, currently choose 5.
271        ActivityManager am = (ActivityManager) ctx
272                .getSystemService(Context.ACTIVITY_SERVICE);
273        if (am.getMemoryClass() > 16) {
274            pageCacheCapacity = 5;
275        } else {
276            pageCacheCapacity = 1;
277        }
278
279    // Load the defaults from the xml
280        // This call is TOO SLOW, need to manually keep the defaults
281        // in sync
282        //PreferenceManager.setDefaultValues(ctx, R.xml.browser_preferences);
283        syncSharedPreferences(ctx, p);
284    }
285
286    /* package */ void syncSharedPreferences(Context ctx, SharedPreferences p) {
287
288        homeUrl =
289            p.getString(PREF_HOMEPAGE, homeUrl);
290        String searchEngineName = p.getString(PREF_SEARCH_ENGINE, null);
291        if (searchEngine == null || !searchEngine.getName().equals(searchEngineName)) {
292            if (searchEngine != null) {
293                if (searchEngine.supportsVoiceSearch()) {
294                    // One or more tabs could have been in voice search mode.
295                    // Clear it, since the new SearchEngine may not support
296                    // it, or may handle it differently.
297                    for (int i = 0; i < mTabControl.getTabCount(); i++) {
298                        mTabControl.getTab(i).revertVoiceSearchMode();
299                    }
300                }
301                searchEngine.close();
302            }
303            searchEngine = SearchEngines.get(ctx, searchEngineName);
304        }
305        Log.i(TAG, "Selected search engine: " + searchEngine);
306
307        loadsImagesAutomatically = p.getBoolean("load_images",
308                loadsImagesAutomatically);
309        javaScriptEnabled = p.getBoolean("enable_javascript",
310                javaScriptEnabled);
311        pluginState = WebSettings.PluginState.valueOf(
312                p.getString("plugin_state", pluginState.name()));
313        javaScriptCanOpenWindowsAutomatically = !p.getBoolean(
314            "block_popup_windows",
315            !javaScriptCanOpenWindowsAutomatically);
316        showSecurityWarnings = p.getBoolean("show_security_warnings",
317                showSecurityWarnings);
318        rememberPasswords = p.getBoolean("remember_passwords",
319                rememberPasswords);
320        saveFormData = p.getBoolean("save_formdata",
321                saveFormData);
322        boolean accept_cookies = p.getBoolean("accept_cookies",
323                CookieManager.getInstance().acceptCookie());
324        CookieManager.getInstance().setAcceptCookie(accept_cookies);
325        openInBackground = p.getBoolean("open_in_background", openInBackground);
326        textSize = WebSettings.TextSize.valueOf(
327                p.getString(PREF_TEXT_SIZE, textSize.name()));
328        zoomDensity = WebSettings.ZoomDensity.valueOf(
329                p.getString(PREF_DEFAULT_ZOOM, zoomDensity.name()));
330        autoFitPage = p.getBoolean("autofit_pages", autoFitPage);
331        loadsPageInOverviewMode = p.getBoolean("load_page",
332                loadsPageInOverviewMode);
333        boolean landscapeOnlyTemp =
334                p.getBoolean("landscape_only", landscapeOnly);
335        if (landscapeOnlyTemp != landscapeOnly) {
336            landscapeOnly = landscapeOnlyTemp;
337        }
338        useWideViewPort = true; // use wide view port for either setting
339        if (autoFitPage) {
340            layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
341        } else {
342            layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL;
343        }
344        defaultTextEncodingName =
345                p.getString(PREF_DEFAULT_TEXT_ENCODING,
346                        defaultTextEncodingName);
347
348        showDebugSettings =
349                p.getBoolean(PREF_DEBUG_SETTINGS, showDebugSettings);
350        // Debug menu items have precidence if the menu is visible
351        if (showDebugSettings) {
352            boolean small_screen = p.getBoolean("small_screen",
353                    layoutAlgorithm ==
354                    WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
355            if (small_screen) {
356                layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN;
357            } else {
358                boolean normal_layout = p.getBoolean("normal_layout",
359                        layoutAlgorithm == WebSettings.LayoutAlgorithm.NORMAL);
360                if (normal_layout) {
361                    layoutAlgorithm = WebSettings.LayoutAlgorithm.NORMAL;
362                } else {
363                    layoutAlgorithm =
364                            WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
365                }
366            }
367            useWideViewPort = p.getBoolean("wide_viewport", useWideViewPort);
368            tracing = p.getBoolean("enable_tracing", tracing);
369            lightTouch = p.getBoolean("enable_light_touch", lightTouch);
370            navDump = p.getBoolean("enable_nav_dump", navDump);
371            userAgent = Integer.parseInt(p.getString("user_agent", "0"));
372        }
373        // JS flags is loaded from DB even if showDebugSettings is false,
374        // so that it can be set once and be effective all the time.
375        jsFlags = p.getString("js_engine_flags", "");
376
377        // Read the setting for showing/hiding the JS Console always so that should the
378        // user enable debug settings, we already know if we should show the console.
379        // The user will never see the console unless they navigate to about:debug,
380        // regardless of the setting we read here. This setting is only used after debug
381        // is enabled.
382        showConsole = p.getBoolean("javascript_console", showConsole);
383
384        // HTML5 API flags
385        appCacheEnabled = p.getBoolean("enable_appcache", appCacheEnabled);
386        databaseEnabled = p.getBoolean("enable_database", databaseEnabled);
387        domStorageEnabled = p.getBoolean("enable_domstorage", domStorageEnabled);
388        geolocationEnabled = p.getBoolean("enable_geolocation", geolocationEnabled);
389        workersEnabled = p.getBoolean("enable_workers", workersEnabled);
390
391        update();
392    }
393
394    public String getHomePage() {
395        return homeUrl;
396    }
397
398    public SearchEngine getSearchEngine() {
399        return searchEngine;
400    }
401
402    public String getJsFlags() {
403        return jsFlags;
404    }
405
406    public WebStorageSizeManager getWebStorageSizeManager() {
407        return webStorageSizeManager;
408    }
409
410    public void setHomePage(Context context, String url) {
411        Editor ed = PreferenceManager.
412                getDefaultSharedPreferences(context).edit();
413        ed.putString(PREF_HOMEPAGE, url);
414        ed.commit();
415        homeUrl = url;
416    }
417
418    public WebSettings.TextSize getTextSize() {
419        return textSize;
420    }
421
422    public WebSettings.ZoomDensity getDefaultZoom() {
423        return zoomDensity;
424    }
425
426    public boolean openInBackground() {
427        return openInBackground;
428    }
429
430    public boolean showSecurityWarnings() {
431        return showSecurityWarnings;
432    }
433
434    public boolean isTracing() {
435        return tracing;
436    }
437
438    public boolean isLightTouch() {
439        return lightTouch;
440    }
441
442    public boolean isNavDump() {
443        return navDump;
444    }
445
446    public boolean showDebugSettings() {
447        return showDebugSettings;
448    }
449
450    public void toggleDebugSettings() {
451        showDebugSettings = !showDebugSettings;
452        navDump = showDebugSettings;
453        update();
454    }
455
456    /**
457     * Add a WebSettings object to the list of observers that will be updated
458     * when update() is called.
459     *
460     * @param s A WebSettings object that is strictly tied to the life of a
461     *            WebView.
462     */
463    public Observer addObserver(WebSettings s) {
464        Observer old = mWebSettingsToObservers.get(s);
465        if (old != null) {
466            super.deleteObserver(old);
467        }
468        Observer o = new Observer(s);
469        mWebSettingsToObservers.put(s, o);
470        super.addObserver(o);
471        return o;
472    }
473
474    /**
475     * Delete the given WebSettings observer from the list of observers.
476     * @param s The WebSettings object to be deleted.
477     */
478    public void deleteObserver(WebSettings s) {
479        Observer o = mWebSettingsToObservers.get(s);
480        if (o != null) {
481            mWebSettingsToObservers.remove(s);
482            super.deleteObserver(o);
483        }
484    }
485
486    /*
487     * Package level method for obtaining a single app instance of the
488     * BrowserSettings.
489     */
490    /*package*/ static BrowserSettings getInstance() {
491        if (sSingleton == null ) {
492            sSingleton = new BrowserSettings();
493        }
494        return sSingleton;
495    }
496
497    /*
498     * Package level method for associating the BrowserSettings with TabControl
499     */
500    /* package */void setTabControl(TabControl tabControl) {
501        mTabControl = tabControl;
502        updateTabControlSettings();
503    }
504
505    /*
506     * Update all the observers of the object.
507     */
508    /*package*/ void update() {
509        setChanged();
510        notifyObservers();
511    }
512
513    /*package*/ void clearCache(Context context) {
514        WebIconDatabase.getInstance().removeAllIcons();
515        if (mTabControl != null) {
516            WebView current = mTabControl.getCurrentWebView();
517            if (current != null) {
518                current.clearCache(true);
519            }
520        }
521    }
522
523    /*package*/ void clearCookies(Context context) {
524        CookieManager.getInstance().removeAllCookie();
525    }
526
527    /* package */void clearHistory(Context context) {
528        ContentResolver resolver = context.getContentResolver();
529        Browser.clearHistory(resolver);
530        Browser.clearSearches(resolver);
531    }
532
533    /* package */ void clearFormData(Context context) {
534        WebViewDatabase.getInstance(context).clearFormData();
535        if (mTabControl != null) {
536            mTabControl.getCurrentTopWebView().clearFormData();
537        }
538    }
539
540    /*package*/ void clearPasswords(Context context) {
541        WebViewDatabase db = WebViewDatabase.getInstance(context);
542        db.clearUsernamePassword();
543        db.clearHttpAuthUsernamePassword();
544    }
545
546    private void updateTabControlSettings() {
547        // Enable/disable the error console.
548        mTabControl.getBrowserActivity().setShouldShowErrorConsole(
549            showDebugSettings && showConsole);
550        mTabControl.getBrowserActivity().setRequestedOrientation(
551            landscapeOnly ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
552            : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
553    }
554
555    private void maybeDisableWebsiteSettings(Context context) {
556        PreferenceActivity activity = (PreferenceActivity) context;
557        final PreferenceScreen screen = (PreferenceScreen)
558            activity.findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
559        screen.setEnabled(false);
560        WebStorage.getInstance().getOrigins(new ValueCallback<Map>() {
561            public void onReceiveValue(Map webStorageOrigins) {
562                if ((webStorageOrigins != null) && !webStorageOrigins.isEmpty()) {
563                    screen.setEnabled(true);
564                }
565            }
566        });
567
568        GeolocationPermissions.getInstance().getOrigins(new ValueCallback<Set<String> >() {
569            public void onReceiveValue(Set<String> geolocationOrigins) {
570                if ((geolocationOrigins != null) && !geolocationOrigins.isEmpty()) {
571                    screen.setEnabled(true);
572                }
573            }
574        });
575    }
576
577    /*package*/ void clearDatabases(Context context) {
578        WebStorage.getInstance().deleteAllData();
579        maybeDisableWebsiteSettings(context);
580    }
581
582    /*package*/ void clearLocationAccess(Context context) {
583        GeolocationPermissions.getInstance().clearAll();
584        maybeDisableWebsiteSettings(context);
585    }
586
587    /*package*/ void resetDefaultPreferences(Context ctx) {
588        reset();
589        SharedPreferences p =
590            PreferenceManager.getDefaultSharedPreferences(ctx);
591        p.edit().clear().commit();
592        PreferenceManager.setDefaultValues(ctx, R.xml.browser_preferences,
593                true);
594        // reset homeUrl
595        setHomePage(ctx, getFactoryResetHomeUrl(ctx));
596        // reset appcache max size
597        appCacheMaxSize = webStorageSizeManager.getAppCacheMaxSize();
598    }
599
600    private String getFactoryResetHomeUrl(Context context) {
601        String url = context.getResources().getString(R.string.homepage_base);
602        if (url.indexOf("{CID}") != -1) {
603            url = url.replace("{CID}",
604                    BrowserProvider.getClientId(context.getContentResolver()));
605        }
606        return url;
607    }
608
609    // Private constructor that does nothing.
610    private BrowserSettings() {
611        reset();
612    }
613
614    private void reset() {
615        // Private variables for settings
616        // NOTE: these defaults need to be kept in sync with the XML
617        // until the performance of PreferenceManager.setDefaultValues()
618        // is improved.
619        loadsImagesAutomatically = true;
620        javaScriptEnabled = true;
621        pluginState = WebSettings.PluginState.ON;
622        javaScriptCanOpenWindowsAutomatically = false;
623        showSecurityWarnings = true;
624        rememberPasswords = true;
625        saveFormData = true;
626        openInBackground = false;
627        autoFitPage = true;
628        landscapeOnly = false;
629        loadsPageInOverviewMode = true;
630        showDebugSettings = false;
631        // HTML5 API flags
632        appCacheEnabled = true;
633        databaseEnabled = true;
634        domStorageEnabled = true;
635        geolocationEnabled = true;
636        workersEnabled = true;  // only affects V8. JSC does not have a similar setting
637    }
638}
639