OSDN Git Service

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