Merge change 8873

* changes:
  More precise determination of when to back up the bookmarks
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b108bbc..0056555 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -333,15 +333,15 @@
     <!-- Confirmation dialog message -->
     <string name="pref_privacy_clear_passwords_dlg">All saved passwords will be cleared.</string>
     <!-- Settings label -->
-    <string name="pref_privacy_request_location">Enable location</string>
+    <string name="pref_privacy_enable_geolocation">Enable location</string>
     <!-- Settings summary -->
-    <string name="pref_privacy_request_location_summary">Allow sites to request access to your location</string>
+    <string name="pref_privacy_enable_geolocation_summary">Allow sites to request access to your location</string>
     <!-- Settings label -->
-    <string name="pref_privacy_clear_location_requests">Clear location access</string>
+    <string name="pref_privacy_clear_geolocation_access">Clear location access</string>
     <!-- Settings summary -->
-    <string name="pref_privacy_clear_location_requests_summary">Clear location access for all websites</string>
+    <string name="pref_privacy_clear_geolocation_access_summary">Clear location access for all websites</string>
     <!-- Confirmation dialog message -->
-    <string name="pref_privacy_clear_location_requests_dlg">Clear all previous location requests</string>
+    <string name="pref_privacy_clear_geolocation_access_dlg">Clear location access for all websites</string>
     <!-- Settings screen, section title -->
     <string name="pref_security_title">Security settings</string>
     <!-- Settings label -->
@@ -767,8 +767,9 @@
     <string name="default_button">OK</string>
 
     <!-- HTML5 dialogs -->
-    <!-- Used as a toast notification after the user close the html5 webstorage permission dialog -->
-    <string name="webstorage_notification">The quota for this site can be changed in the Local Storage section of the Browser settings</string>
+    <!-- Used as a status bar notification when the browser is running out of space trying to allocate or increase the database quota for an HTML5 databae or application cache. -->
+    <string name="webstorage_outofspace_notification_title">Browser storage full</string>
+    <string name="webstorage_outofspace_notification_text">Click to free up space.</string>
     <!-- Used in the Browser Settings -->
     <string name="webstorage_clear_data_title">Clear stored data</string>
     <string name="webstorage_clear_data_summary">Remove all databases associated with this website</string>
@@ -780,6 +781,17 @@
     <!-- Strings used in the summary of origins -->
     <string name="webstorage_origin_summary_mb_stored">MB stored on your phone</string>
 
+    <!-- Geolocation -->
+    <!-- Settings page, Advanced Settings -> Website settings -> <origin> -->
+    <string name="geolocation_settings_page_title">Clear location access</string>
+    <string name="geolocation_settings_page_summary_allowed">This site can currently access your location</string>
+    <string name="geolocation_settings_page_summary_not_allowed">This site cannot currently access your location</string>
+    <!-- Settings page dialog -->
+    <string name="geolocation_settings_page_dialog_title">Clear location access</string>
+    <string name="geolocation_settings_page_dialog_message">Location access for this website will be cleared</string>
+    <string name="geolocation_settings_page_dialog_ok_button">Clear access</string>
+    <string name="geolocation_settings_page_dialog_cancel_button">Cancel</string>
+
     <!-- Zoom-related strings --><skip />
     <!-- Caption for a button that is shown when the zoom widget is showing.  The button's action will switch to the zoom overview mode. -->
     <string name="zoom_overview_button_text">Overview</string>
@@ -788,4 +800,5 @@
     <string name="error_console_header_text_maximized" translatable="false">JavaScript Console</string>
     <string name="error_console_eval_text_hint" translatable="false">Evaluate JavaScript</string>
     <string name="error_console_eval_button_text" translatable="false">Evaluate</string>
+
 </resources>
diff --git a/res/xml/browser_preferences.xml b/res/xml/browser_preferences.xml
index dffb550..83c492e 100644
--- a/res/xml/browser_preferences.xml
+++ b/res/xml/browser_preferences.xml
@@ -134,23 +134,20 @@
                 android:dialogTitle="@string/clear" 
                 android:dialogIcon="@android:drawable/ic_dialog_alert"/>
 
-        <!-- below preferences will be shown when html5 location is implemented -->
-        <!--
-
         <CheckBoxPreference
-                android:key="request_location"
+                android:key="enable_geolocation"
                 android:defaultValue="true"
-                android:title="@string/pref_privacy_request_location"
-                android:summary="@string/pref_privacy_request_location_summary" />
+                android:title="@string/pref_privacy_enable_geolocation"
+                android:summary="@string/pref_privacy_enable_geolocation_summary" />
 
         <com.android.browser.BrowserYesNoPreference
-                android:key="privacy_clear_location_requests"
-                android:title="@string/pref_privacy_clear_location_requests"
-                android:summary="@string/pref_privacy_clear_location_requests_summary"
-                android:dialogMessage="@string/pref_privacy_clear_location_requests_dlg"
+                android:key="privacy_clear_geolocation_access"
+                android:dependency="enable_geolocation"
+                android:title="@string/pref_privacy_clear_geolocation_access"
+                android:summary="@string/pref_privacy_clear_geolocation_access_summary"
+                android:dialogMessage="@string/pref_privacy_clear_geolocation_access_dlg"
                 android:dialogTitle="@string/clear"
                 android:dialogIcon="@android:drawable/ic_dialog_alert"/>
-        -->
 
     </PreferenceCategory>
     
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 7f40494..a2b5c88 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -894,10 +894,11 @@
             urlData.setPostData(intent
                     .getByteArrayExtra(Browser.EXTRA_POST_DATA));
 
-            if (Intent.ACTION_VIEW.equals(action) &&
-                    (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
-                final String appId =
-                        intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
+            final String appId = intent
+                    .getStringExtra(Browser.EXTRA_APPLICATION_ID);
+            if (Intent.ACTION_VIEW.equals(action)
+                    && !getPackageName().equals(appId)
+                    && (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
                 TabControl.Tab appTab = mTabControl.getTabFromId(appId);
                 if (appTab != null) {
                     Log.i(LOGTAG, "Reusing tab for " + appId);
@@ -1038,6 +1039,7 @@
         if (appData != null) {
             intent.putExtra(SearchManager.APP_DATA, appData);
         }
+        intent.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName());
         startActivity(intent);
 
         return true;
diff --git a/src/com/android/browser/BrowserPreferencesPage.java b/src/com/android/browser/BrowserPreferencesPage.java
index 4536b2c..2348af0 100644
--- a/src/com/android/browser/BrowserPreferencesPage.java
+++ b/src/com/android/browser/BrowserPreferencesPage.java
@@ -29,10 +29,10 @@
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceScreen;
 import android.util.Log;
+import android.webkit.GeolocationPermissions;
 import android.webkit.Plugin;
 import android.webkit.WebStorage;
 import android.webkit.WebView;
-import android.webkit.Plugin;
 
 public class BrowserPreferencesPage extends PreferenceActivity
         implements Preference.OnPreferenceChangeListener, 
@@ -69,35 +69,36 @@
 
         e = findPreference(BrowserSettings.PREF_DEFAULT_TEXT_ENCODING);
         e.setOnPreferenceChangeListener(this);
-        
+
         if (BrowserSettings.getInstance().showDebugSettings()) {
             addPreferencesFromResource(R.xml.debug_preferences);
         }
-        
+
         e = findPreference(BrowserSettings.PREF_GEARS_SETTINGS);
         e.setOnPreferenceClickListener(this);
 
-        PreferenceScreen manageDatabases = (PreferenceScreen)
+        PreferenceScreen websiteSettings = (PreferenceScreen)
             findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
         Intent intent = new Intent(this, WebsiteSettingsActivity.class);
-        manageDatabases.setIntent(intent);
+        websiteSettings.setIntent(intent);
     }
 
     /*
-     * We need to set the manageDatabases PreferenceScreen state
-     * in the onResume(), as the number of origins with databases
-     * could have changed after calling the WebsiteSettingsActivity.
+     * We need to set the PreferenceScreen state in onResume(), as the number of
+     * origins with active features (WebStorage, Geolocation etc) could have
+     * changed after calling the WebsiteSettingsActivity.
      */
     @Override
     protected void onResume() {
         super.onResume();
-        PreferenceScreen manageDatabases = (PreferenceScreen)
+        PreferenceScreen websiteSettings = (PreferenceScreen)
             findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
-        manageDatabases.setEnabled(false);
-        Set origins = WebStorage.getInstance().getOrigins();
-        if ((origins != null) && (origins.size() > 0)) {
-            manageDatabases.setEnabled(true);
-        }
+        Set webStorageOrigins = WebStorage.getInstance().getOrigins();
+        Set geolocationOrigins =
+            GeolocationPermissions.getInstance().getOrigins();
+        websiteSettings.setEnabled(
+            ((webStorageOrigins != null) && !webStorageOrigins.isEmpty()) ||
+            ((geolocationOrigins != null) && !geolocationOrigins.isEmpty()));
     }
 
     @Override
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 79deb61..3ed6cf0 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -26,6 +26,7 @@
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceScreen;
 import android.webkit.CookieManager;
+import android.webkit.GeolocationPermissions;
 import android.webkit.WebView;
 import android.webkit.WebViewDatabase;
 import android.webkit.WebIconDatabase;
@@ -34,6 +35,7 @@
 import android.preference.PreferenceManager;
 import android.provider.Browser;
 
+import java.util.Set;
 import java.util.HashMap;
 import java.util.Observable;
 
@@ -81,6 +83,7 @@
     private WebStorageSizeManager webStorageSizeManager;
     private boolean domStorageEnabled = true;
     private String jsFlags = "";
+    private boolean geolocationEnabled = true;
 
     private final static String TAG = "BrowserSettings";
 
@@ -130,6 +133,8 @@
     public final static String PREF_DEFAULT_ZOOM = "default_zoom";
     public final static String PREF_DEFAULT_TEXT_ENCODING =
             "default_text_encoding";
+    public final static String PREF_CLEAR_LOCATION_ACCESS =
+            "privacy_clear_location_access";
 
     private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (Macintosh; " +
             "U; Intel Mac OS X 10_5_7; en-us) AppleWebKit/530.17 (KHTML, " +
@@ -216,6 +221,10 @@
             // Enable/Disable the error console.
             b.mTabControl.getBrowserActivity().setShouldShowErrorConsole(
                     b.showDebugSettings && b.showConsole);
+
+            // Configure the Geolocation permissions manager to deny all
+            // permission requests if Geolocation is disabled in the browser.
+            // TODO(steveblock): Implement
         }
     }
 
@@ -352,6 +361,8 @@
         mTabControl.getBrowserActivity().setShouldShowErrorConsole(
                 showDebugSettings && showConsole);
 
+        geolocationEnabled = p.getBoolean("enable_geolocation", geolocationEnabled);
+
         update();
     }
 
@@ -522,14 +533,27 @@
         db.clearHttpAuthUsernamePassword();
     }
 
+    private void maybeDisableWebsiteSettings(Context context) {
+        Set webStorageOrigins = WebStorage.getInstance().getOrigins();
+        Set geolocationOrigins =
+                 GeolocationPermissions.getInstance().getOrigins();
+        if (((webStorageOrigins == null) || webStorageOrigins.isEmpty()) &&
+            ((geolocationOrigins == null) || geolocationOrigins.isEmpty())) {
+            PreferenceActivity activity = (PreferenceActivity) context;
+            PreferenceScreen screen = (PreferenceScreen)
+                activity.findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
+            screen.setEnabled(false);
+        }
+    }
+
     /*package*/ void clearDatabases(Context context) {
         WebStorage.getInstance().deleteAllData();
-        // Remove all listed databases from the preferences
-        PreferenceActivity activity = (PreferenceActivity) context;
-        PreferenceScreen screen = (PreferenceScreen)
-            activity.findPreference(BrowserSettings.PREF_WEBSITE_SETTINGS);
-        screen.removeAll();
-        screen.setEnabled(false);
+        maybeDisableWebsiteSettings(context);
+    }
+
+    /*package*/ void clearLocationAccess(Context context) {
+        GeolocationPermissions.getInstance().clearAll();
+        maybeDisableWebsiteSettings(context);
     }
 
     /*package*/ void resetDefaultPreferences(Context ctx) {
diff --git a/src/com/android/browser/BrowserYesNoPreference.java b/src/com/android/browser/BrowserYesNoPreference.java
index ae93882..e380e57 100644
--- a/src/com/android/browser/BrowserYesNoPreference.java
+++ b/src/com/android/browser/BrowserYesNoPreference.java
@@ -51,6 +51,9 @@
                     getKey())) {
                 BrowserSettings.getInstance().resetDefaultPreferences(context);
                 setEnabled(true);
+            } else if (BrowserSettings.PREF_CLEAR_LOCATION_ACCESS.equals(
+                    getKey())) {
+                BrowserSettings.getInstance().clearLocationAccess(context);
             }
         }
     }
diff --git a/src/com/android/browser/WebStorageSizeManager.java b/src/com/android/browser/WebStorageSizeManager.java
index ff21ea9..40d30a2 100644
--- a/src/com/android/browser/WebStorageSizeManager.java
+++ b/src/com/android/browser/WebStorageSizeManager.java
@@ -16,7 +16,11 @@
 
 package com.android.browser;
 
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.os.StatFs;
 import android.util.Log;
 import android.webkit.WebStorage;
@@ -84,7 +88,6 @@
  * a system notification that will guide the user to the WebSettings UI. There,
  * the user can free some of the Web storage space by deleting all the data used
  * by an origin.
- * TODO(andreip): implement the notification.
  */
 class WebStorageSizeManager {
     // Logging flags.
@@ -95,6 +98,14 @@
     public final static long ORIGIN_DEFAULT_QUOTA = 3 * 1024 * 1024;  // 3MB
     // The default value for quota increases.
     public final static long QUOTA_INCREASE_STEP = 1 * 1024 * 1024;  // 1MB
+    // Extra padding space for appcache maximum size increases. This is needed
+    // because WebKit sends us an estimate of the amount of space needed
+    // but this estimate may, currently, be slightly less than what is actually
+    // needed. We therefore add some 'padding'.
+    // TODO(andreip): fix this in WebKit.
+    public final static long APPCACHE_MAXSIZE_PADDING = 512 * 1024; // 512KB
+    // The system status bar notification id.
+    private final static int OUT_OF_SPACE_ID = 1;
     // The application context.
     private final Context mContext;
     // The global Web storage limit.
@@ -222,8 +233,16 @@
 
         if (totalUnusedQuota <= 0) {
             // There definitely isn't any more space. Fire notifications
-            // and exit.
-            scheduleOutOfSpaceNotification();
+            // if needed and exit.
+            if (totalUsedQuota > 0) {
+                // We only fire the notification if there are some other websites
+                // using some of the quota. This avoids the degenerate case where
+                // the first ever website to use Web storage tries to use more
+                // data than it is actually available. In such a case, showing
+                // the notification would not help at all since there is nothing
+                // the user can do.
+                scheduleOutOfSpaceNotification();
+            }
             quotaUpdater.updateQuota(currentQuota);
             if(LOGV_ENABLED) {
                 Log.v(LOGTAG, "onExceededDatabaseQuota: out of space.");
@@ -268,10 +287,18 @@
 
         long totalUnusedQuota = mGlobalLimit - totalUsedQuota - mAppCacheMaxSize;
 
-        if (totalUnusedQuota < spaceNeeded) {
+        if (totalUnusedQuota < spaceNeeded + APPCACHE_MAXSIZE_PADDING) {
             // There definitely isn't any more space. Fire notifications
-            // and exit.
-            scheduleOutOfSpaceNotification();
+            // if needed and exit.
+            if (totalUsedQuota > 0) {
+                // We only fire the notification if there are some other websites
+                // using some of the quota. This avoids the degenerate case where
+                // the first ever website to use Web storage tries to use more
+                // data than it is actually available. In such a case, showing
+                // the notification would not help at all since there is nothing
+                // the user can do.
+                scheduleOutOfSpaceNotification();
+            }
             quotaUpdater.updateQuota(0);
             if(LOGV_ENABLED) {
                 Log.v(LOGTAG, "onReachedMaxAppCacheSize: out of space.");
@@ -279,7 +306,7 @@
             return;
         }
         // There is enough space to accommodate spaceNeeded bytes.
-        mAppCacheMaxSize += spaceNeeded;
+        mAppCacheMaxSize += spaceNeeded + APPCACHE_MAXSIZE_PADDING;
         quotaUpdater.updateQuota(mAppCacheMaxSize);
 
         if(LOGV_ENABLED) {
@@ -324,6 +351,32 @@
     // Schedules a system notification that takes the user to the WebSettings
     // activity when clicked.
     private void scheduleOutOfSpaceNotification() {
-        // TODO(andreip): implement.
+        if(LOGV_ENABLED) {
+            Log.v(LOGTAG, "scheduleOutOfSpaceNotification called.");
+        }
+        if (mContext == null) {
+            // mContext can be null if we're running unit tests.
+            return;
+        }
+        // setup the notification boilerplate.
+        int icon = R.drawable.ic_launcher_browser;
+        CharSequence title = mContext.getString(
+                R.string.webstorage_outofspace_notification_title);
+        CharSequence text = mContext.getString(
+                R.string.webstorage_outofspace_notification_text);
+        long when = System.currentTimeMillis();
+        Intent intent = new Intent(mContext, WebsiteSettingsActivity.class);
+        PendingIntent contentIntent =
+            PendingIntent.getActivity(mContext, 0, intent, 0);
+        Notification notification = new Notification(icon, title, when);
+        notification.setLatestEventInfo(mContext, title, text, contentIntent);
+        notification.flags |= Notification.FLAG_AUTO_CANCEL;
+        // Fire away.
+        String ns = Context.NOTIFICATION_SERVICE;
+        NotificationManager mgr =
+            (NotificationManager) mContext.getSystemService(ns);
+        if (mgr != null) {
+            mgr.notify(OUT_OF_SPACE_ID, notification);
+        }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/browser/WebsiteSettingsActivity.java b/src/com/android/browser/WebsiteSettingsActivity.java
index f91879f..89e5963 100644
--- a/src/com/android/browser/WebsiteSettingsActivity.java
+++ b/src/com/android/browser/WebsiteSettingsActivity.java
@@ -31,6 +31,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.webkit.GeolocationPermissions;
 import android.webkit.WebIconDatabase;
 import android.webkit.WebStorage;
 import android.widget.ArrayAdapter;
@@ -42,12 +43,14 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import java.util.Vector;
 
 /**
  * Manage the settings for an origin.
- * We use it to keep track of the HTML5 settings, i.e. database (webstorage).
+ * We use it to keep track of the 'HTML5' settings, i.e. database (webstorage)
+ * and Geolocation.
  */
 public class WebsiteSettingsActivity extends ListActivity {
 
@@ -59,11 +62,59 @@
         private String mOrigin;
         private String mTitle;
         private Bitmap mIcon;
+        private int mFeatures;
+
+        // These constants provide the set of features that a site may support
+        // They must be consecutive. To add a new feature, add a new FEATURE_XXX
+        // variable with value equal to the current value of FEATURE_COUNT, then
+        // increment FEATURE_COUNT.
+        private final static int FEATURE_WEB_STORAGE = 0;
+        private final static int FEATURE_GEOLOCATION = 1;
+        // The number of features available.
+        private final static int FEATURE_COUNT = 2;
 
         public Site(String origin) {
             mOrigin = origin;
             mTitle = null;
             mIcon = null;
+            mFeatures = 0;
+        }
+
+        public void addFeature(int feature) {
+            mFeatures |= (1 << feature);
+        }
+
+        public boolean hasFeature(int feature) {
+            return (mFeatures & (1 << feature)) != 0;
+        }
+
+        /**
+         * Gets the number of features supported by this site.
+         */
+        public int getFeatureCount() {
+            int count = 0;
+            for (int i = 0; i < FEATURE_COUNT; ++i) {
+                count += hasFeature(i) ? 1 : 0;
+            }
+            return count;
+        }
+
+        /**
+         * Gets the ID of the nth (zero-based) feature supported by this site.
+         * The return value is a feature ID - one of the FEATURE_XXX values.
+         * This is required to determine which feature is displayed at a given
+         * position in the list of features for this site. This is used both
+         * when populating the view and when responding to clicks on the list.
+         */
+        public int getFeatureByIndex(int n) {
+            int j = -1;
+            for (int i = 0; i < FEATURE_COUNT; ++i) {
+                j += hasFeature(i) ? 1 : 0;
+                if (j == n) {
+                    return i;
+                }
+            }
+            return -1;
         }
 
         public String getOrigin() {
@@ -102,7 +153,6 @@
         private LayoutInflater mInflater;
         private Bitmap mDefaultIcon;
         private Site mCurrentSite;
-        private final static int STORED_DATA = 0;
 
         public SiteAdapter(Context context, int rsc) {
             super(context, rsc);
@@ -113,28 +163,57 @@
             populateOrigins();
         }
 
+        /**
+         * Adds the specified feature to the site corresponding to supplied
+         * origin in the map. Creates the site if it does not already exist.
+         */
+        private void addFeatureToSite(Map sites, String origin, int feature) {
+            Site site = null;
+            if (sites.containsKey(origin)) {
+                site = (Site) sites.get(origin);
+            } else {
+                site = new Site(origin);
+                sites.put(origin, site);
+            }
+            site.addFeature(feature);
+        }
+
         public void populateOrigins() {
             clear();
 
-            // Get the list of origins we want to display
+            // Get the list of origins we want to display.
+            // All 'HTML 5 modules' (Database, Geolocation etc) form these
+            // origin strings using WebCore::SecurityOrigin::toString(), so it's
+            // safe to group origins here. Note that WebCore::SecurityOrigin
+            // uses 0 (which is not printed) for the port if the port is the
+            // default for the protocol. Eg http://www.google.com and
+            // http://www.google.com:80 both record a port of 0 and hence
+            // toString() == 'http://www.google.com' for both.
             Set origins = WebStorage.getInstance().getOrigins();
-            Set sites = new HashSet<Site>();
+            Map sites = new HashMap<String, Site>();
             if (origins != null) {
                 Iterator<String> iter = origins.iterator();
                 while (iter.hasNext()) {
-                    String origin = iter.next();
-                    Site site = new Site(origin);
-                    sites.add(site);
+                    addFeatureToSite(sites, iter.next(), Site.FEATURE_WEB_STORAGE);
+                }
+            }
+            origins = GeolocationPermissions.getInstance().getOrigins();
+            if (origins != null) {
+                Iterator<String> iter = origins.iterator();
+                while (iter.hasNext()) {
+                    addFeatureToSite(sites, iter.next(), Site.FEATURE_GEOLOCATION);
                 }
             }
 
             // Create a map from host to origin. This is used to add metadata
             // (title, icon) for this origin from the bookmarks DB.
             HashMap hosts = new HashMap<String, Set<Site> >();
-            Iterator<Site> sitesIter = sites.iterator();
-            while (sitesIter.hasNext()) {
-                Site site = sitesIter.next();
-                String host = Uri.parse(site.getOrigin()).getHost();
+            Set keys = sites.keySet();
+            Iterator<String> originIter = keys.iterator();
+            while (originIter.hasNext()) {
+                String origin = originIter.next();
+                Site site = (Site) sites.get(origin);
+                String host = Uri.parse(origin).getHost();
                 Set hostSites = null;
                 if (hosts.containsKey(host)) {
                     hostSites = (Set) hosts.get(host);
@@ -166,7 +245,7 @@
                             bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
                         }
                         Set matchingSites = (Set) hosts.get(host);
-                        sitesIter = matchingSites.iterator();
+                        Iterator<Site> sitesIter = matchingSites.iterator();
                         while (sitesIter.hasNext()) {
                             Site site = sitesIter.next();
                             site.setTitle(title);
@@ -179,9 +258,11 @@
             }
 
             // We can now simply populate our array with Site instances
-            sitesIter = sites.iterator();
-            while (sitesIter.hasNext()) {
-                Site site = sitesIter.next();
+            keys = sites.keySet();
+            originIter = keys.iterator();
+            while (originIter.hasNext()) {
+                String origin = originIter.next();
+                Site site = (Site) sites.get(origin);
                 add(site);
             }
 
@@ -194,7 +275,7 @@
             if (mCurrentSite == null) {
                 return super.getCount();
             }
-            return 1; // db view
+            return mCurrentSite.getFeatureCount();
         }
 
         public String sizeValueToString(long value) {
@@ -254,13 +335,22 @@
                 view.setTag(site);
             } else {
                 icon.setVisibility(View.GONE);
-                if (position == STORED_DATA) {
-                    String origin = mCurrentSite.getOrigin();
-                    long usageValue = WebStorage.getInstance().getUsageForOrigin(origin);
-                    String usage = sizeValueToString(usageValue) + " " + sMBStored;
+                String origin = mCurrentSite.getOrigin();
+                switch (mCurrentSite.getFeatureByIndex(position)) {
+                    case Site.FEATURE_WEB_STORAGE:
+                        long usageValue = WebStorage.getInstance().getUsageForOrigin(origin);
+                        String usage = sizeValueToString(usageValue) + " " + sMBStored;
 
-                    title.setText(R.string.webstorage_clear_data_title);
-                    subtitle.setText(usage);
+                        title.setText(R.string.webstorage_clear_data_title);
+                        subtitle.setText(usage);
+                        break;
+                    case Site.FEATURE_GEOLOCATION:
+                        title.setText(R.string.geolocation_settings_page_title);
+                        boolean allowed = GeolocationPermissions.getInstance().getAllowed(origin);
+                        subtitle.setText(allowed ?
+                                         R.string.geolocation_settings_page_summary_allowed :
+                                         R.string.geolocation_settings_page_summary_not_allowed);
+                        break;
                 }
             }
 
@@ -272,21 +362,39 @@
                                 int position,
                                 long id) {
             if (mCurrentSite != null) {
-                if (position == STORED_DATA) {
-                    new AlertDialog.Builder(getContext())
-                        .setTitle(R.string.webstorage_clear_data_dialog_title)
-                        .setMessage(R.string.webstorage_clear_data_dialog_message)
-                        .setPositiveButton(R.string.webstorage_clear_data_dialog_ok_button,
-                                           new AlertDialog.OnClickListener() {
-                            public void onClick(DialogInterface dlg, int which) {
-                                WebStorage.getInstance().deleteOrigin(mCurrentSite.getOrigin());
-                                mCurrentSite = null;
-                                populateOrigins();
-                                notifyDataSetChanged();
-                            }})
-                        .setNegativeButton(R.string.webstorage_clear_data_dialog_cancel_button, null)
-                        .setIcon(android.R.drawable.ic_dialog_alert)
-                        .show();
+                switch (mCurrentSite.getFeatureByIndex(position)) {
+                    case Site.FEATURE_WEB_STORAGE:
+                        new AlertDialog.Builder(getContext())
+                            .setTitle(R.string.webstorage_clear_data_dialog_title)
+                            .setMessage(R.string.webstorage_clear_data_dialog_message)
+                            .setPositiveButton(R.string.webstorage_clear_data_dialog_ok_button,
+                                               new AlertDialog.OnClickListener() {
+                                public void onClick(DialogInterface dlg, int which) {
+                                    WebStorage.getInstance().deleteOrigin(mCurrentSite.getOrigin());
+                                    mCurrentSite = null;
+                                    populateOrigins();
+                                    notifyDataSetChanged();
+                                }})
+                            .setNegativeButton(R.string.webstorage_clear_data_dialog_cancel_button, null)
+                            .setIcon(android.R.drawable.ic_dialog_alert)
+                            .show();
+                        break;
+                    case Site.FEATURE_GEOLOCATION:
+                        new AlertDialog.Builder(getContext())
+                            .setTitle(R.string.geolocation_settings_page_dialog_title)
+                            .setMessage(R.string.geolocation_settings_page_dialog_message)
+                            .setPositiveButton(R.string.geolocation_settings_page_dialog_ok_button,
+                                               new AlertDialog.OnClickListener() {
+                                public void onClick(DialogInterface dlg, int which) {
+                                    GeolocationPermissions.getInstance().clear(mCurrentSite.getOrigin());
+                                    mCurrentSite = null;
+                                    populateOrigins();
+                                    notifyDataSetChanged();
+                                }})
+                            .setNegativeButton(R.string.geolocation_settings_page_dialog_cancel_button, null)
+                            .setIcon(android.R.drawable.ic_dialog_alert)
+                            .show();
+                        break;
                 }
             } else {
                 mCurrentSite = (Site) view.getTag();