Merge "Update homescreen icon generator to look better on xlarge devices."
diff --git a/Android.mk b/Android.mk
index 2abde9b..a5a47b3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -4,7 +4,8 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-        android-common
+        android-common \
+        guava
 
 LOCAL_SRC_FILES := \
         $(call all-java-files-under, src) \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b653461..955687b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -41,7 +41,7 @@
                    android:label="@string/application_name"
                    android:icon="@mipmap/ic_launcher_browser"
                    android:backupAgent=".BrowserBackupAgent"
-                   android:hardwareAccelerated="false"
+                   android:hardwareAccelerated="true"
                    android:taskAffinity="android.task.browser" >
 
         <provider android:name="BrowserProvider"
@@ -66,6 +66,7 @@
                   android:alwaysRetainTaskState="true"
                   android:configChanges="orientation|keyboardHidden"
                   android:theme="@style/BrowserTheme"
+                  android:hardwareAccelerated="false"
                   android:windowSoftInputMode="adjustResize" >
             <intent-filter>
                 <action android:name="android.speech.action.VOICE_SEARCH_RESULTS" />
@@ -160,18 +161,13 @@
                   android:configChanges="orientation|keyboardHidden">
         </activity>
 
-        <activity android:name="BrowserPreferencesPage" android:label="@string/menu_preferences"
-                  android:configChanges="orientation|keyboardHidden">
+        <activity android:name="BrowserPreferencesPage" android:label="@string/menu_preferences">
         </activity>
 
         <activity android:name="BrowserHistoryPage" android:label=""
                   android:configChanges="orientation|keyboardHidden">
         </activity>
 
-        <activity android:name="WebsiteSettingsActivity" android:label=""
-                  android:configChanges="orientation|keyboardHidden">
-        </activity>
-
         <activity android:name="BookmarkSearch"
                   android:label="@string/bookmarks_search"
                   android:stateNotNeeded="true"
diff --git a/res/drawable-hdpi/frame_bookmark_thumb_pressed.9.png b/res/drawable-hdpi/frame_bookmark_thumb_pressed.9.png
new file mode 100644
index 0000000..95f3ab5
--- /dev/null
+++ b/res/drawable-hdpi/frame_bookmark_thumb_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi/frame_bookmark_thumb_pressed.9.png b/res/drawable-mdpi/frame_bookmark_thumb_pressed.9.png
new file mode 100755
index 0000000..8bae932
--- /dev/null
+++ b/res/drawable-mdpi/frame_bookmark_thumb_pressed.9.png
Binary files differ
diff --git a/res/menu/websitesettings.xml b/res/drawable/bookmark_thumb_selector.xml
similarity index 62%
copy from res/menu/websitesettings.xml
copy to res/drawable/bookmark_thumb_selector.xml
index 5acc8a5..5219d90 100644
--- a/res/menu/websitesettings.xml
+++ b/res/drawable/bookmark_thumb_selector.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2009 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,9 +14,8 @@
      limitations under the License.
 -->
 
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/website_settings_menu_clear_all"
-          android:title="@string/website_settings_clear_all"
-          android:icon="@android:drawable/ic_menu_close_clear_cancel" />
-</menu>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/frame_bookmark_thumb_pressed" />
+    <item android:drawable="@android:color/transparent" />
+</selector>
 
diff --git a/res/layout/autofill_settings_fragment.xml b/res/layout/autofill_settings_fragment.xml
index bdffebb..b0e0f53 100644
--- a/res/layout/autofill_settings_fragment.xml
+++ b/res/layout/autofill_settings_fragment.xml
@@ -38,6 +38,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_weight="1"
+        android:shrinkColumns="1"
         android:stretchColumns="1">
         <TableRow
             android:layout_height="match_parent"
diff --git a/res/layout/bookmarks.xml b/res/layout/bookmarks.xml
index 7a0aa23..28f83e4 100644
--- a/res/layout/bookmarks.xml
+++ b/res/layout/bookmarks.xml
@@ -37,7 +37,7 @@
             android:verticalSpacing="16dip"
             android:stretchMode="spacingWidth"
             android:scrollbarStyle="insideInset"
-            android:listSelector="@android:drawable/gallery_thumb"
+            android:listSelector="@drawable/bookmark_thumb_selector"
             android:drawSelectorOnTop="true"
             android:focusable="true"
             android:focusableInTouchMode="true"
diff --git a/res/layout/browser_add_bookmark.xml b/res/layout/browser_add_bookmark.xml
index 0eaf526..c547389 100644
--- a/res/layout/browser_add_bookmark.xml
+++ b/res/layout/browser_add_bookmark.xml
@@ -69,6 +69,7 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:stretchColumns="1"
+        android:shrinkColumns="1"
         android:paddingTop="20dip"
         android:paddingLeft="20dip"
         android:paddingRight="20dip" >
diff --git a/res/layout/url_bar.xml b/res/layout/url_bar.xml
index 2a67c72..ba93d1b 100644
--- a/res/layout/url_bar.xml
+++ b/res/layout/url_bar.xml
@@ -63,7 +63,7 @@
                 android:layout_height="match_parent"
                 style="@style/HoloIcon"
                 android:visibility="gone" />
-            <EditText
+            <TextView
                 android:id="@+id/url_unfocused"
                 android:layout_width="0dip"
                 android:layout_weight="1.0"
@@ -76,9 +76,7 @@
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:lines="1"
-                android:scrollHorizontally="true"
-                android:inputType="textUri"
-                android:imeOptions="actionGo" />
+                android:scrollHorizontally="true" />
             <ImageButton
                 android:id="@+id/star"
                 android:src="@drawable/ic_favorite_off_normal"
diff --git a/res/layout/website_settings.xml b/res/layout/website_settings.xml
new file mode 100644
index 0000000..ed95148
--- /dev/null
+++ b/res/layout/website_settings.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:background="@android:color/transparent">
+
+    <ListView android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        android:clipToPadding="false"
+        android:drawSelectorOnTop="false"
+        android:cacheColorHint="@android:color/transparent"
+        android:scrollbarAlwaysDrawVerticalTrack="true" />
+
+    <Button android:id="@+id/clear_all_button"
+            android:layout_width="150dip"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:text="@string/website_settings_clear_all"
+            android:visibility="gone" />
+</LinearLayout>
diff --git a/res/menu/websitesettings.xml b/res/menu/url_selection.xml
similarity index 69%
rename from res/menu/websitesettings.xml
rename to res/menu/url_selection.xml
index 5acc8a5..1bb6d0e 100644
--- a/res/menu/websitesettings.xml
+++ b/res/menu/url_selection.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2009 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -14,8 +15,10 @@
 -->
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/website_settings_menu_clear_all"
-          android:title="@string/website_settings_clear_all"
-          android:icon="@android:drawable/ic_menu_close_clear_cancel" />
+    <item android:id="@+id/share"
+        android:icon="@drawable/ic_menu_share_normal"
+        android:title="@string/menu_share_url"
+        android:showAsAction="always|withText"
+        />
 </menu>
 
diff --git a/res/values-id/strings.xml b/res/values-in/strings.xml
similarity index 100%
rename from res/values-id/strings.xml
rename to res/values-in/strings.xml
diff --git a/res/values-he/strings.xml b/res/values-iw/strings.xml
similarity index 100%
rename from res/values-he/strings.xml
rename to res/values-iw/strings.xml
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e98d9d5..ece6ffa 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -871,4 +871,10 @@
 
     <!-- Button allowing users to import all of their existing bookmarks into an account when setting up syncing with their bookmarks stored in Google Chrome [CHAR-LIMIT=64] -->
     <string name="import_bookmarks_dialog_import">Add your Android bookmarks to bookmarks for <xliff:g id="Google account" example="account@example.com">%s</xliff:g></string>
+
+    <!-- Url Selection Action Mode -->
+
+    <!-- Menu item to share URL selection [CHAR LIMIT=30] -->
+    <string name="menu_share_url">Share</string>
+
 </resources>
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index 5f8944f..79c3327 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -201,6 +201,9 @@
         if (mActiveTabsPage != null) {
             mUiController.removeActiveTabsPage(true);
         }
+        if (isCustomViewShowing()) {
+            onHideCustomView();
+        }
         cancelStopToast();
         mActivityPaused = true;
     }
@@ -427,6 +430,24 @@
         mainView.setEmbeddedTitleBar(null);
     }
 
+    @Override
+    public void onSetWebView(Tab tab, WebView webView) {
+        View container = tab.getViewContainer();
+        if (container == null) {
+            // The tab consists of a container view, which contains the main
+            // WebView, as well as any other UI elements associated with the tab.
+            container = mActivity.getLayoutInflater().inflate(R.layout.tab,
+                    null);
+            tab.setViewContainer(container);
+        }
+        if (tab.getWebView() != webView) {
+            // Just remove the old one.
+            FrameLayout wrapper =
+                    (FrameLayout) container.findViewById(R.id.webview_wrapper);
+            wrapper.removeView(tab.getWebView());
+        }
+    }
+
     /**
      * create a sub window container and webview for the tab
      * Note: this methods operates through side-effects for now
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index 9844547..a8c393a 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.browser;
 
+import com.google.common.annotations.VisibleForTesting;
+
 import android.app.Activity;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -95,16 +97,11 @@
         mController.start(icicle, getIntent());
     }
 
+    @VisibleForTesting
     Controller getController() {
         return mController;
     }
 
-    // TODO: this is here for the test classes
-    // remove once tests are fixed
-    TabControl getTabControl() {
-        return mController.getTabControl();
-    }
-
     @Override
     protected void onNewIntent(Intent intent) {
         mController.handleNewIntent(intent);
diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java
index 4887f3f..74385f9 100644
--- a/src/com/android/browser/BrowserBookmarksPage.java
+++ b/src/com/android/browser/BrowserBookmarksPage.java
@@ -357,8 +357,6 @@
         }
         BookmarkItem header = new BookmarkItem(activity);
         populateBookmarkItem(cursor, header, isFolder);
-        new LookupBookmarkCount(getActivity(), header)
-                .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
         menu.setHeaderView(header);
 
         int count = menu.size();
@@ -374,6 +372,8 @@
             Bitmap bitmap =
                 BitmapFactory.decodeResource(getResources(), R.drawable.ic_folder);
             item.setFavicon(bitmap);
+            new LookupBookmarkCount(getActivity(), item)
+                    .execute(cursor.getLong(BookmarksLoader.COLUMN_INDEX_ID));
         } else {
             String url = cursor.getString(BookmarksLoader.COLUMN_INDEX_URL);
             item.setUrl(url);
diff --git a/src/com/android/browser/BrowserHistoryPage.java b/src/com/android/browser/BrowserHistoryPage.java
index a3ce0b4..5cd9545 100644
--- a/src/com/android/browser/BrowserHistoryPage.java
+++ b/src/com/android/browser/BrowserHistoryPage.java
@@ -366,7 +366,10 @@
                 mMostVisited.close();
             }
             mMostVisited = cursor;
-            mMostVisited.registerDataSetObserver(mDataSetObserver);
+            if (mMostVisited != null) {
+                mMostVisited.registerDataSetObserver(mDataSetObserver);
+            }
+            notifyDataSetChanged();
         }
 
         @Override
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index acd76dd..75dd913 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -317,6 +317,11 @@
     }
 
     @Override
+    public void onSetWebView(Tab tab, WebView view) {
+        mUi.onSetWebView(tab, view);
+    }
+
+    @Override
     public void createSubWindow(Tab tab) {
         endActionMode();
         WebView mainView = tab.getWebView();
@@ -488,6 +493,22 @@
 
     }
 
+    @Override
+    public void shareCurrentPage() {
+        shareCurrentPage(mTabControl.getCurrentTab());
+    }
+
+    private void shareCurrentPage(Tab tab) {
+        if (tab != null) {
+            tab.populatePickerData();
+            sharePage(mActivity, tab.getTitle(),
+                    tab.getUrl(), tab.getFavicon(),
+                    createScreenshot(tab.getWebView(),
+                            getDesiredThumbnailWidth(mActivity),
+                            getDesiredThumbnailHeight(mActivity)));
+        }
+    }
+
     /**
      * Share a page, providing the title, url, favicon, and a screenshot.  Uses
      * an {@link Intent} to launch the Activity chooser.
@@ -543,13 +564,16 @@
             Log.e(LOGTAG, "BrowserActivity is already paused.");
             return;
         }
-        mTabControl.pauseCurrentTab();
+        CookieManager.getInstance().flushCookieStore();
         mActivityPaused = true;
-        if (mTabControl.getCurrentIndex() >= 0 &&
-                !pauseWebViewTimers(mActivityPaused)) {
-            mWakeLock.acquire();
-            mHandler.sendMessageDelayed(mHandler
-                    .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
+        Tab tab = mTabControl.getCurrentTab();
+        if (tab != null) {
+            tab.pause();
+            if (!pauseWebViewTimers(tab)) {
+                mWakeLock.acquire();
+                mHandler.sendMessageDelayed(mHandler
+                        .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
+            }
         }
         mUi.onPause();
         mNetworkHandler.onPause();
@@ -575,10 +599,12 @@
             Log.e(LOGTAG, "BrowserActivity is already resumed.");
             return;
         }
-        mTabControl.resumeCurrentTab();
         mActivityPaused = false;
-        resumeWebViewTimers();
-
+        Tab current = mTabControl.getCurrentTab();
+        if (current != null) {
+            current.resume();
+            resumeWebViewTimers(current);
+        }
         if (mWakeLock.isHeld()) {
             mHandler.removeMessages(RELEASE_WAKELOCK);
             mWakeLock.release();
@@ -588,9 +614,11 @@
         WebView.enablePlatformNotifications();
     }
 
-    private void resumeWebViewTimers() {
-        Tab tab = mTabControl.getCurrentTab();
-        if (tab == null) return; // monkey can trigger this
+    /**
+     * resume all WebView timers using the WebView instance of the given tab
+     * @param tab guaranteed non-null
+     */
+    private void resumeWebViewTimers(Tab tab) {
         boolean inLoad = tab.inPageLoad();
         if ((!mActivityPaused && !inLoad) || (mActivityPaused && inLoad)) {
             CookieSyncManager.getInstance().startSync();
@@ -601,19 +629,23 @@
         }
     }
 
-    private boolean pauseWebViewTimers(boolean activityPaused) {
-        Tab tab = mTabControl.getCurrentTab();
-        boolean inLoad = tab.inPageLoad();
-        if (activityPaused && !inLoad) {
+    /**
+     * Pause all WebView timers using the WebView of the given tab
+     * @param tab
+     * @return true if the timers are paused or tab is null
+     */
+    private boolean pauseWebViewTimers(Tab tab) {
+        if (tab == null) {
+            return true;
+        } else if (!tab.inPageLoad()) {
             CookieSyncManager.getInstance().stopSync();
             WebView w = getCurrentWebView();
             if (w != null) {
                 w.pauseTimers();
             }
             return true;
-        } else {
-            return false;
         }
+        return false;
     }
 
     void onDestroy() {
@@ -708,7 +740,7 @@
         // to start the timer. As we won't switch tabs while an activity is in
         // pause state, we can ensure calling resume and pause in pair.
         if (mActivityPaused) {
-            resumeWebViewTimers();
+            resumeWebViewTimers(tab);
         }
         mLoadStopped = false;
         if (!mNetworkHandler.isNetworkUp()) {
@@ -749,7 +781,7 @@
         }
         // pause the WebView timer and release the wake lock if it is finished
         // while BrowserActivity is in pause state.
-        if (mActivityPaused && pauseWebViewTimers(mActivityPaused)) {
+        if (mActivityPaused && pauseWebViewTimers(tab)) {
             if (mWakeLock.isHeld()) {
                 mHandler.removeMessages(RELEASE_WAKELOCK);
                 mWakeLock.release();
@@ -1565,12 +1597,7 @@
                     mCanChord = false;
                     return false;
                 }
-                currentTab.populatePickerData();
-                sharePage(mActivity, currentTab.getTitle(),
-                        currentTab.getUrl(), currentTab.getFavicon(),
-                        createScreenshot(currentTab.getWebView(),
-                                getDesiredThumbnailWidth(mActivity),
-                                getDesiredThumbnailHeight(mActivity)));
+                shareCurrentPage(currentTab);
                 break;
 
             case R.id.dump_nav_menu_id:
@@ -2312,7 +2339,7 @@
                     // force the tab's inLoad() to be false as we are going to
                     // either finish the activity or remove the tab. This will
                     // ensure pauseWebViewTimers() taking action.
-                    mTabControl.getCurrentTab().clearInPageLoad();
+                    current.clearInPageLoad();
                     if (mTabControl.getTabCount() == 1) {
                         mActivity.finish();
                         return;
@@ -2321,7 +2348,7 @@
                         Log.e(LOGTAG, "BrowserActivity is already paused "
                                 + "while handing goBackOnePageOrQuit.");
                     }
-                    pauseWebViewTimers(true);
+                    pauseWebViewTimers(current);
                     removeTab(current);
                 }
                 /*
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index a048c2d..8a3bc27 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -85,7 +85,7 @@
     // The Geolocation permissions prompt
     private GeolocationPermissionsPrompt mGeolocationPermissionsPrompt;
     // Main WebView wrapper
-    private LinearLayout mContainer;
+    private View mContainer;
     // Main WebView
     private WebView mMainView;
     // Subwindow container
@@ -128,8 +128,6 @@
     // the lock icon type and previous lock icon type for the tab
     private int mLockIconType;
     private int mPrevLockIconType;
-    // Inflation service for making subwindows.
-    private final LayoutInflater mInflateService;
     // The listener that gets invoked when a download is started from the
     // mMainView
     private final DownloadListener mDownloadListener;
@@ -497,8 +495,10 @@
 
         @Override
         public void onPageFinished(WebView view, String url) {
-            LogTag.logPageFinishedLoading(
-                    url, SystemClock.uptimeMillis() - mLoadStartTime);
+            if (!isPrivateBrowsingEnabled()) {
+                LogTag.logPageFinishedLoading(
+                        url, SystemClock.uptimeMillis() - mLoadStartTime);
+            }
             mInPageLoad = false;
 
             mWebViewController.onPageFinished(Tab.this, url);
@@ -1203,11 +1203,6 @@
         mInPageLoad = false;
         mInForeground = false;
 
-        mInflateService = LayoutInflater.from(mActivity);
-
-        // The tab consists of a container view, which contains the main
-        // WebView, as well as any other UI elements associated with the tab.
-        mContainer = (LinearLayout) mInflateService.inflate(R.layout.tab, null);
 
         mDownloadListener = new DownloadListener() {
             public void onDownloadStart(String url, String userAgent,
@@ -1244,16 +1239,14 @@
         if (mMainView == w) {
             return;
         }
+
         // If the WebView is changing, the page will be reloaded, so any ongoing
         // Geolocation permission requests are void.
         if (mGeolocationPermissionsPrompt != null) {
             mGeolocationPermissionsPrompt.hide();
         }
 
-        // Just remove the old one.
-        FrameLayout wrapper =
-                (FrameLayout) mContainer.findViewById(R.id.webview_wrapper);
-        wrapper.removeView(mMainView);
+        mWebViewController.onSetWebView(this, w);
 
         // set the new one
         mMainView = w;
@@ -1449,6 +1442,10 @@
         return mMainView;
     }
 
+    void setViewContainer(View container) {
+        mContainer = container;
+    }
+
     View getViewContainer() {
         return mContainer;
     }
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index 2d90d23..9e669ce 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -600,20 +600,6 @@
         return setCurrentTab(newTab, false);
     }
 
-    void pauseCurrentTab() {
-        Tab t = getCurrentTab();
-        if (t != null) {
-            t.pause();
-        }
-    }
-
-    void resumeCurrentTab() {
-        Tab t = getCurrentTab();
-        if (t != null) {
-            t.resume();
-        }
-    }
-
     /**
      * If force is true, this method skips the check for newTab == current.
      */
diff --git a/src/com/android/browser/TitleBarXLarge.java b/src/com/android/browser/TitleBarXLarge.java
index 7e54710..0aa09db 100644
--- a/src/com/android/browser/TitleBarXLarge.java
+++ b/src/com/android/browser/TitleBarXLarge.java
@@ -26,12 +26,9 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
-import android.view.ContextMenu;
 import android.view.LayoutInflater;
-import android.view.MenuInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -39,11 +36,10 @@
  * tabbed title bar for xlarge screen browser
  */
 public class TitleBarXLarge extends TitleBarBase
-    implements UrlInputListener, OnClickListener, OnFocusChangeListener {
+    implements UrlInputListener, OnClickListener {
 
     private static final int PROGRESS_MAX = 100;
 
-    private Activity mActivity;
     private UiController mUiController;
 
     private Drawable mStopDrawable;
@@ -67,7 +63,6 @@
 
     public TitleBarXLarge(Activity activity, UiController controller) {
         super(activity);
-        mActivity = activity;
         mUiController = controller;
         Resources resources = activity.getResources();
         mStopDrawable = resources.getDrawable(R.drawable.ic_stop_normal);
@@ -106,25 +101,15 @@
         mGoButton.setOnClickListener(this);
         mClearButton.setOnClickListener(this);
         mUrlFocused.setUrlInputListener(this);
-        mUrlUnfocused.setOnFocusChangeListener(this);
         mUrlFocused.setContainer(mFocusContainer);
+        mUrlFocused.setController(mUiController);
         mUnfocusContainer.setOnClickListener(this);
     }
 
-    public void onFocusChange(View v, boolean hasFocus) {
-        if (hasFocus) {
-            setUrlMode(true);
-            mUrlFocused.selectAll();
-            mUrlFocused.requestFocus();
-            mUrlFocused.setDropDownWidth(mUnfocusContainer.getWidth());
-            mUrlFocused.setDropDownHorizontalOffset(-mUrlFocused.getLeft());
-        }
-    }
-
     @Override
     public void onClick(View v) {
         if (mUnfocusContainer == v) {
-            mUrlUnfocused.requestFocus();
+            setUrlMode(true);
         } else if (mBackButton == v) {
             mUiController.getCurrentTopWebView().goBack();
         } else if (mForwardButton == v) {
@@ -190,6 +175,10 @@
     private void setUrlMode(boolean focused) {
         swapUrlContainer(focused);
         if (focused) {
+            mUrlFocused.selectAll();
+            mUrlFocused.requestFocus();
+            mUrlFocused.setDropDownWidth(mUnfocusContainer.getWidth());
+            mUrlFocused.setDropDownHorizontalOffset(-mUrlFocused.getLeft());
             mSearchButton.setVisibility(View.GONE);
             mGoButton.setVisibility(View.VISIBLE);
         } else {
@@ -203,13 +192,6 @@
         mFocusContainer.setVisibility(focus ? View.VISIBLE : View.GONE);
     }
 
-    @Override
-    public void createContextMenu(ContextMenu menu) {
-        MenuInflater inflater = mActivity.getMenuInflater();
-        inflater.inflate(R.menu.title_context, menu);
-        mActivity.onCreateContextMenu(menu, this, null);
-    }
-
     private void search() {
         setDisplayTitle("");
         mUrlUnfocused.requestFocus();
diff --git a/src/com/android/browser/UI.java b/src/com/android/browser/UI.java
index 3a8a5cd..e7f67f2 100644
--- a/src/com/android/browser/UI.java
+++ b/src/com/android/browser/UI.java
@@ -58,6 +58,8 @@
 
     public void attachTab(Tab tab);
 
+    public void onSetWebView(Tab tab, WebView view);
+
     public void createSubWindow(Tab tab, WebView subWebView);
 
     public void attachSubWindow(View subContainer);
diff --git a/src/com/android/browser/UiController.java b/src/com/android/browser/UiController.java
index dffebba..c74d74e 100644
--- a/src/com/android/browser/UiController.java
+++ b/src/com/android/browser/UiController.java
@@ -75,4 +75,6 @@
 
     void endActionMode();
 
+    void shareCurrentPage();
+
 }
diff --git a/src/com/android/browser/UrlInputView.java b/src/com/android/browser/UrlInputView.java
index 2e29f26..a4c2be3 100644
--- a/src/com/android/browser/UrlInputView.java
+++ b/src/com/android/browser/UrlInputView.java
@@ -20,8 +20,8 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.ActionMode;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.View.OnFocusChangeListener;
@@ -35,7 +35,8 @@
  * handling suggestions
  */
 public class UrlInputView extends AutoCompleteTextView
-        implements OnFocusChangeListener, OnEditorActionListener, CompletionListener {
+        implements OnFocusChangeListener, OnEditorActionListener,
+        CompletionListener {
 
     private UrlInputListener   mListener;
     private InputMethodManager mInputManager;
@@ -65,11 +66,17 @@
         super.setOnFocusChangeListener(this);
         mAdapter = new SuggestionsAdapter(ctx, this);
         setAdapter(mAdapter);
-        setSelectAllOnFocus(false);
+        setSelectAllOnFocus(true);
         onConfigurationChanged(ctx.getResources().getConfiguration());
         setThreshold(1);
     }
 
+    void setController(UiController controller) {
+        UrlSelectionActionMode urlSelectionMode
+                = new UrlSelectionActionMode(controller);
+        setCustomSelectionActionModeCallback(urlSelectionMode);
+    }
+
     void setContainer(View container) {
         mContainer = container;
     }
@@ -108,12 +115,6 @@
     }
 
     @Override
-    public ActionMode startActionMode(ActionMode.Callback callback) {
-        // suppress selection action mode
-        return null;
-    }
-
-    @Override
     public void setOnFocusChangeListener(OnFocusChangeListener focusListener) {
         mWrappedFocusListener = focusListener;
     }
@@ -148,7 +149,7 @@
         this.dismissDropDown();
         this.setSelection(0,0);
         mInputManager.hideSoftInputFromWindow(getWindowToken(), 0);
-        if (url == null) {
+        if (TextUtils.isEmpty(url)) {
             mListener.onDismiss();
         } else {
             mListener.onAction(url, extra);
diff --git a/src/com/android/browser/UrlSelectionActionMode.java b/src/com/android/browser/UrlSelectionActionMode.java
new file mode 100644
index 0000000..5636388
--- /dev/null
+++ b/src/com/android/browser/UrlSelectionActionMode.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.browser;
+
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class UrlSelectionActionMode implements ActionMode.Callback {
+
+    private UiController mUiController;
+
+    public UrlSelectionActionMode(UiController controller) {
+        mUiController = controller;
+    }
+
+    // ActionMode.Callback implementation
+
+    @Override
+    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+        mode.getMenuInflater().inflate(R.menu.url_selection, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.share:
+                mUiController.shareCurrentPage();
+                mode.finish();
+                break;
+            default:
+                return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void onDestroyActionMode(ActionMode mode) {
+    }
+
+    @Override
+    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+        return true;
+    }
+
+}
diff --git a/src/com/android/browser/WebStorageSizeManager.java b/src/com/android/browser/WebStorageSizeManager.java
index dcf2f8b..5f76f72 100644
--- a/src/com/android/browser/WebStorageSizeManager.java
+++ b/src/com/android/browser/WebStorageSizeManager.java
@@ -16,17 +16,19 @@
 
 package com.android.browser;
 
+import com.android.browser.preferences.WebsiteSettingsFragment;
+
 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.preference.PreferenceActivity;
 import android.util.Log;
 import android.webkit.WebStorage;
 
 import java.io.File;
-import java.util.Set;
 
 
 /**
@@ -82,7 +84,7 @@
  * the user can free some of the Web storage space by deleting all the data used
  * by an origin.
  */
-class WebStorageSizeManager {
+public class WebStorageSizeManager {
     // Logging flags.
     private final static boolean LOGV_ENABLED = com.android.browser.Browser.LOGV_ENABLED;
     private final static boolean LOGD_ENABLED = com.android.browser.Browser.LOGD_ENABLED;
@@ -346,7 +348,7 @@
     // Reset the notification time; we use this iff the user
     // use clear all; we reset it to some time in the future instead
     // of just setting it to -1, as the clear all method is asynchronous
-    static void resetLastOutOfSpaceNotificationTime() {
+    public static void resetLastOutOfSpaceNotificationTime() {
         mLastOutOfSpaceNotificationTime = System.currentTimeMillis() -
             NOTIFICATION_INTERVAL + RESET_NOTIFICATION_INTERVAL;
     }
@@ -403,7 +405,9 @@
             CharSequence text = mContext.getString(
                     R.string.webstorage_outofspace_notification_text);
             long when = System.currentTimeMillis();
-            Intent intent = new Intent(mContext, WebsiteSettingsActivity.class);
+            Intent intent = new Intent(mContext, BrowserPreferencesPage.class);
+            intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT,
+                    WebsiteSettingsFragment.class.getName());
             PendingIntent contentIntent =
                 PendingIntent.getActivity(mContext, 0, intent, 0);
             Notification notification = new Notification(icon, title, when);
diff --git a/src/com/android/browser/WebViewController.java b/src/com/android/browser/WebViewController.java
index eeeee18..11a6959 100644
--- a/src/com/android/browser/WebViewController.java
+++ b/src/com/android/browser/WebViewController.java
@@ -42,6 +42,8 @@
 
     WebViewFactory getWebViewFactory();
 
+    void onSetWebView(Tab tab, WebView view);
+
     void createSubWindow(Tab tab);
 
     void onPageStarted(Tab tab, WebView view, String url, Bitmap favicon);
diff --git a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
index 59b6ce1..952e04a 100644
--- a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
+++ b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
@@ -18,9 +18,7 @@
 
 import com.android.browser.BrowserSettings;
 import com.android.browser.R;
-import com.android.browser.WebsiteSettingsActivity;
 
-import android.content.Intent;
 import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
@@ -44,8 +42,7 @@
 
         PreferenceScreen websiteSettings = (PreferenceScreen) findPreference(
                 BrowserSettings.PREF_WEBSITE_SETTINGS);
-        Intent intent = new Intent(getActivity(), WebsiteSettingsActivity.class);
-        websiteSettings.setIntent(intent);
+        websiteSettings.setFragment(WebsiteSettingsFragment.class.getName());
     }
 
     /*
diff --git a/src/com/android/browser/preferences/PersonalPreferencesFragment.java b/src/com/android/browser/preferences/PersonalPreferencesFragment.java
index a0c8ea0..0620df2 100644
--- a/src/com/android/browser/preferences/PersonalPreferencesFragment.java
+++ b/src/com/android/browser/preferences/PersonalPreferencesFragment.java
@@ -81,14 +81,14 @@
         refreshUi(context);
     }
 
-    private class GetAccountsTask extends AsyncTask<Void, Void, Void> {
+    private class GetAccountsTask extends AsyncTask<Void, Void, String> {
         private Context mContext;
 
         GetAccountsTask(Context ctx) {
             mContext = ctx;
         }
 
-        protected Void doInBackground(Void... unused) {
+        protected String doInBackground(Void... unused) {
             AccountManager am = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
             Account[] accounts = am.getAccountsByType("com.google");
             if (accounts == null || accounts.length == 0) {
@@ -97,26 +97,34 @@
                     getPreferenceScreen().removePreference(mChromeSync);
                 }
             } else {
+                // Google accounts are present.
                 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
                 Bundle args = mChromeSync.getExtras();
                 args.putParcelableArray("accounts", accounts);
                 mEnabled = BrowserContract.Settings.isSyncEnabled(mContext);
+                mChromeSync.setOnPreferenceClickListener(PersonalPreferencesFragment.this);
+
                 if (!mEnabled) {
-                    // Google accounts are present, but Chrome sync isn't enabled yet.
                     // Setup a link to the enable wizard
-                    mChromeSync.setSummary(R.string.pref_personal_sync_with_chrome_summary);
+                    return mContext.getResources().getString(
+                            R.string.pref_personal_sync_with_chrome_summary);
                 } else {
                     // Chrome sync is enabled, setup a link to account switcher
-                    String accountName = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_NAME,
-                            null);
-                    mChromeSync.setSummary(accountName);
+                    String accountName = prefs.getString(
+                            BrowserBookmarksPage.PREF_ACCOUNT_NAME, null);
                     args.putString("curAccount", accountName);
+                    return accountName;
                 }
-                mChromeSync.setOnPreferenceClickListener(PersonalPreferencesFragment.this);
             }
 
             return null;
         }
+
+        protected void onPostExecute(String summary) {
+            if (summary != null) {
+                mChromeSync.setSummary(summary);
+            }
+        }
     }
 
     void refreshUi(Context context) {
diff --git a/src/com/android/browser/WebsiteSettingsActivity.java b/src/com/android/browser/preferences/WebsiteSettingsFragment.java
similarity index 86%
rename from src/com/android/browser/WebsiteSettingsActivity.java
rename to src/com/android/browser/preferences/WebsiteSettingsFragment.java
index 95f8fb0..1965ffe 100644
--- a/src/com/android/browser/WebsiteSettingsActivity.java
+++ b/src/com/android/browser/preferences/WebsiteSettingsFragment.java
@@ -14,56 +14,62 @@
  * limitations under the License.
  */
 
-package com.android.browser;
+package com.android.browser.preferences;
+
+import com.android.browser.R;
+import com.android.browser.WebStorageSizeManager;
 
 import android.app.AlertDialog;
-import android.app.ListActivity;
+import android.app.FragmentTransaction;
+import android.app.ListFragment;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
 import android.provider.BrowserContract.Bookmarks;
 import android.util.Log;
-import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.webkit.GeolocationPermissions;
 import android.webkit.ValueCallback;
-import android.webkit.WebIconDatabase;
 import android.webkit.WebStorage;
-import android.widget.ArrayAdapter;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import java.io.Serializable;
 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)
  * and Geolocation.
  */
-public class WebsiteSettingsActivity extends ListActivity {
+public class WebsiteSettingsFragment extends ListFragment implements OnClickListener {
 
+    private static final String EXTRA_SITE = "site";
     private String LOGTAG = "WebsiteSettingsActivity";
     private static String sMBStored = null;
     private SiteAdapter mAdapter = null;
+    private Site mSite = null;
 
-    static class Site {
+    static class Site implements Serializable {
         private String mOrigin;
         private String mTitle;
         private Bitmap mIcon;
@@ -169,6 +175,10 @@
         private Site mCurrentSite;
 
         public SiteAdapter(Context context, int rsc) {
+            this(context, rsc, null);
+        }
+
+        public SiteAdapter(Context context, int rsc, Site site) {
             super(context, rsc);
             mResource = rsc;
             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -184,7 +194,10 @@
                     R.drawable.ic_list_gps_on);
             mLocationDisallowedIcon = BitmapFactory.decodeResource(getResources(),
                     R.drawable.ic_list_gps_denied);
-            askForOrigins();
+            mCurrentSite = site;
+            if (mCurrentSite == null) {
+                askForOrigins();
+            }
         }
 
         /**
@@ -409,6 +422,7 @@
             }
         }
 
+        @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             View view;
             final TextView title;
@@ -434,7 +448,6 @@
             locationIcon.setVisibility(View.GONE);
 
             if (mCurrentSite == null) {
-                setTitle(getString(R.string.pref_extras_website_settings));
 
                 Site site = getItem(position);
                 title.setText(site.getPrettyTitle());
@@ -494,7 +507,6 @@
                 locationIcon.setVisibility(View.GONE);
                 usageIcon.setVisibility(View.GONE);
                 featureIcon.setVisibility(View.VISIBLE);
-                setTitle(mCurrentSite.getPrettyTitle());
                 String origin = mCurrentSite.getOrigin();
                 switch (mCurrentSite.getFeatureByIndex(position)) {
                     case Site.FEATURE_WEB_STORAGE:
@@ -551,7 +563,7 @@
                                     // origins list.
                                     mCurrentSite.removeFeature(Site.FEATURE_WEB_STORAGE);
                                     if (mCurrentSite.getFeatureCount() == 0) {
-                                        mCurrentSite = null;
+                                        finish();
                                     }
                                     askForOrigins();
                                     notifyDataSetChanged();
@@ -570,7 +582,7 @@
                                     GeolocationPermissions.getInstance().clear(mCurrentSite.getOrigin());
                                     mCurrentSite.removeFeature(Site.FEATURE_GEOLOCATION);
                                     if (mCurrentSite.getFeatureCount() == 0) {
-                                        mCurrentSite = null;
+                                        finish();
                                     }
                                     askForOrigins();
                                     notifyDataSetChanged();
@@ -581,8 +593,14 @@
                         break;
                 }
             } else {
-                mCurrentSite = (Site) view.getTag();
-                notifyDataSetChanged();
+                Site site = (Site) view.getTag();
+                PreferenceActivity activity = (PreferenceActivity) getActivity();
+                if (activity != null) {
+                    Bundle args = new Bundle();
+                    args.putSerializable(EXTRA_SITE, site);
+                    activity.startPreferencePanel(WebsiteSettingsFragment.class.getName(), args, 0,
+                            site.getPrettyTitle(), null, 0);
+                }
             }
         }
 
@@ -591,67 +609,64 @@
         }
     }
 
-    /**
-     * Intercepts the back key to immediately notify
-     * NativeDialog that we are done.
-     */
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if ((event.getKeyCode() == KeyEvent.KEYCODE_BACK)
-            && (event.getAction() == KeyEvent.ACTION_DOWN)) {
-            if ((mAdapter != null) && (mAdapter.backKeyPressed())){
-                return true; // event consumed
-            }
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.website_settings, container, false);
+        Bundle args = getArguments();
+        if (args != null) {
+            mSite = (Site) args.getSerializable(EXTRA_SITE);
         }
-        return super.dispatchKeyEvent(event);
+        if (mSite == null) {
+            View clear = view.findViewById(R.id.clear_all_button);
+            clear.setVisibility(View.VISIBLE);
+            clear.setOnClickListener(this);
+        }
+        return view;
     }
 
     @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
         if (sMBStored == null) {
             sMBStored = getString(R.string.webstorage_origin_summary_mb_stored);
         }
-        mAdapter = new SiteAdapter(this, R.layout.website_settings_row);
-        setListAdapter(mAdapter);
+        mAdapter = new SiteAdapter(getActivity(), R.layout.website_settings_row);
+        if (mSite != null) {
+            mAdapter.mCurrentSite = mSite;
+        }
+        getListView().setAdapter(mAdapter);
         getListView().setOnItemClickListener(mAdapter);
     }
 
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        MenuInflater inflater = getMenuInflater();
-        inflater.inflate(R.menu.websitesettings, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        // If we are not on the sites list (rather on the page for a specific site) or
-        // we aren't listing any sites hide the clear all button (and hence the menu).
-        return  mAdapter.currentSite() == null && mAdapter.getCount() > 0;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.website_settings_menu_clear_all:
-                // Show the prompt to clear all origins of their data and geolocation permissions.
-                new AlertDialog.Builder(this)
-                        .setTitle(R.string.website_settings_clear_all_dialog_title)
-                        .setMessage(R.string.website_settings_clear_all_dialog_message)
-                        .setPositiveButton(R.string.website_settings_clear_all_dialog_ok_button,
-                                new AlertDialog.OnClickListener() {
-                                    public void onClick(DialogInterface dlg, int which) {
-                                        WebStorage.getInstance().deleteAllData();
-                                        GeolocationPermissions.getInstance().clearAll();
-                                        WebStorageSizeManager.resetLastOutOfSpaceNotificationTime();
-                                        mAdapter.askForOrigins();
-                                        finish();
-                                    }})
-                        .setNegativeButton(R.string.website_settings_clear_all_dialog_cancel_button, null)
-                        .setIcon(android.R.drawable.ic_dialog_alert)
-                        .show();
-                return true;
+    private void finish() {
+        PreferenceActivity activity = (PreferenceActivity) getActivity();
+        if (activity != null) {
+            activity.finishPreferencePanel(this, 0, null);
         }
-        return false;
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+        case R.id.clear_all_button:
+         // Show the prompt to clear all origins of their data and geolocation permissions.
+            new AlertDialog.Builder(getActivity())
+                    .setTitle(R.string.website_settings_clear_all_dialog_title)
+                    .setMessage(R.string.website_settings_clear_all_dialog_message)
+                    .setPositiveButton(R.string.website_settings_clear_all_dialog_ok_button,
+                            new AlertDialog.OnClickListener() {
+                                public void onClick(DialogInterface dlg, int which) {
+                                    WebStorage.getInstance().deleteAllData();
+                                    GeolocationPermissions.getInstance().clearAll();
+                                    WebStorageSizeManager.resetLastOutOfSpaceNotificationTime();
+                                    mAdapter.askForOrigins();
+                                    finish();
+                                }})
+                    .setNegativeButton(R.string.website_settings_clear_all_dialog_cancel_button, null)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .show();
+            break;
+        }
     }
 }
diff --git a/src/com/android/browser/widget/BookmarkListWidgetProvider.java b/src/com/android/browser/widget/BookmarkListWidgetProvider.java
index 04f7b07..2196ae0 100644
--- a/src/com/android/browser/widget/BookmarkListWidgetProvider.java
+++ b/src/com/android/browser/widget/BookmarkListWidgetProvider.java
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
-import android.util.Log;
 import android.widget.RemoteViews;
 
 /**
diff --git a/src/com/android/browser/widget/BookmarkListWidgetService.java b/src/com/android/browser/widget/BookmarkListWidgetService.java
index 14b52b7..8fbe807 100644
--- a/src/com/android/browser/widget/BookmarkListWidgetService.java
+++ b/src/com/android/browser/widget/BookmarkListWidgetService.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.graphics.Bitmap;
@@ -31,9 +32,8 @@
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapFactory.Options;
 import android.net.Uri;
-import android.os.Binder;
+import android.os.AsyncTask;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.preference.PreferenceManager;
 import android.provider.BrowserContract;
 import android.provider.BrowserContract.Bookmarks;
@@ -101,7 +101,6 @@
             BookmarkFactory fac = mFactories.get(widgetId);
             if (fac != null && folderId >= 0) {
                 fac.changeFolder(folderId);
-                AppWidgetManager.getInstance(this).notifyAppWidgetViewDataChanged(widgetId, R.id.bookmarks_list);
             }
         }
         return START_STICKY;
@@ -123,10 +122,11 @@
             super.onChange(selfChange);
 
             // Update all the bookmark widgets
-            sendBroadcast(new Intent(
-                    BookmarkListWidgetProvider.ACTION_BOOKMARK_APPWIDGET_UPDATE,
-                    null, BookmarkListWidgetService.this,
-                    BookmarkListWidgetProvider.class));
+            if (mFactories != null) {
+                for (BookmarkFactory fac : mFactories.values()) {
+                    fac.loadData();
+                }
+            }
         }
     }
 
@@ -137,7 +137,10 @@
             Log.w(TAG, "Missing EXTRA_APPWIDGET_ID!");
             return null;
         } else {
-            BookmarkFactory fac = new BookmarkFactory(this, widgetId);
+            BookmarkFactory fac = mFactories.get(widgetId);
+            if (fac == null) {
+                fac = new BookmarkFactory(getApplicationContext(), widgetId);
+            }
             mFactories.put(widgetId, fac);
             return fac;
         }
@@ -152,13 +155,15 @@
         }
     }
 
-    static class BookmarkFactory implements RemoteViewsService.RemoteViewsFactory {
+    static class BookmarkFactory implements RemoteViewsService.RemoteViewsFactory,
+            OnSharedPreferenceChangeListener {
         private List<RenderResult> mBookmarks;
         private Context mContext;
         private int mWidgetId;
         private String mAccountType;
         private String mAccountName;
         private Stack<Breadcrumb> mBreadcrumbs;
+        private LoadBookmarksTask mLoadTask;
 
         public BookmarkFactory(Context context, int widgetId) {
             mBreadcrumbs = new Stack<Breadcrumb>();
@@ -171,12 +176,14 @@
 
             if (!mBreadcrumbs.empty() && mBreadcrumbs.peek().mId == folderId) {
                 mBreadcrumbs.pop();
+                loadData();
                 return;
             }
 
             for (RenderResult res : mBookmarks) {
                 if (res.mId == folderId) {
                     mBreadcrumbs.push(new Breadcrumb(res.mId, res.mTitle));
+                    loadData();
                     break;
                 }
             }
@@ -264,30 +271,55 @@
             SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
             mAccountType = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, null);
             mAccountName = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, null);
+            prefs.registerOnSharedPreferenceChangeListener(this);
             loadData();
         }
 
         @Override
         public void onDestroy() {
             recycleBitmaps();
+            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+            prefs.unregisterOnSharedPreferenceChangeListener(this);
         }
 
         @Override
         public void onDataSetChanged() {
-            loadData();
         }
 
         void loadData() {
-            // Reset identity since this could be an IPC call
-            long token = Binder.clearCallingIdentity();
-            update();
-            Binder.restoreCallingIdentity(token);
+            if (mLoadTask != null) {
+                mLoadTask.cancel(false);
+            }
+            mLoadTask = new LoadBookmarksTask();
+            mLoadTask.execute();
         }
 
-        void update() {
-            recycleBitmaps();
+        class LoadBookmarksTask extends AsyncTask<Void, Void, List<RenderResult>> {
+            private Breadcrumb mFolder;
+
+            @Override
+            protected void onPreExecute() {
+                mFolder = mBreadcrumbs.empty() ? null : mBreadcrumbs.peek();
+            }
+
+            @Override
+            protected List<RenderResult> doInBackground(Void... params) {
+                return loadBookmarks(mFolder);
+            }
+
+            @Override
+            protected void onPostExecute(List<RenderResult> result) {
+                if (!isCancelled() && result != null) {
+                    recycleBitmaps();
+                    mBookmarks = result;
+                    AppWidgetManager.getInstance(mContext)
+                            .notifyAppWidgetViewDataChanged(mWidgetId, R.id.bookmarks_list);
+                }
+            }
+        }
+
+        List<RenderResult> loadBookmarks(Breadcrumb folder) {
             String where = null;
-            Breadcrumb folder = mBreadcrumbs.empty() ? null : mBreadcrumbs.peek();
             Uri uri;
             if (USE_FOLDERS) {
                 uri = BrowserContract.Bookmarks.CONTENT_URI_DEFAULT_FOLDER;
@@ -301,18 +333,21 @@
             if (!TextUtils.isEmpty(mAccountType) && !TextUtils.isEmpty(mAccountName)) {
                 uri = uri.buildUpon()
                         .appendQueryParameter(Bookmarks.PARAM_ACCOUNT_TYPE, mAccountType)
-                        .appendQueryParameter(Bookmarks.PARAM_ACCOUNT_NAME, mAccountName).build();
+                        .appendQueryParameter(Bookmarks.PARAM_ACCOUNT_NAME, mAccountName)
+                        .build();
             }
             Cursor c = null;
             try {
                 c = mContext.getContentResolver().query(uri, PROJECTION,
                         where, null, null);
                 if (c != null) {
-                    mBookmarks = new ArrayList<RenderResult>(c.getCount() + 1);
+                    ArrayList<RenderResult> bookmarks
+                            = new ArrayList<RenderResult>(c.getCount() + 1);
                     if (folder != null) {
-                        RenderResult res = new RenderResult(folder.mId, folder.mTitle, null);
+                        RenderResult res = new RenderResult(
+                                folder.mId, folder.mTitle, null);
                         res.mIsFolder = true;
-                        mBookmarks.add(res);
+                        bookmarks.add(res);
                     }
                     while (c.moveToNext()) {
                         long id = c.getLong(0);
@@ -328,8 +363,9 @@
                                     blob, 0, blob.length, options);
                         }
                         res.mIsFolder = c.getInt(4) != 0;
-                        mBookmarks.add(res);
+                        bookmarks.add(res);
                     }
+                    return bookmarks;
                 }
             } catch (IllegalStateException e) {
                 Log.e(TAG, "update bookmark widget", e);
@@ -338,6 +374,7 @@
                     c.close();
                 }
             }
+            return null;
         }
 
         private void recycleBitmaps() {
@@ -351,6 +388,21 @@
                 }
             }
         }
+
+        @Override
+        public void onSharedPreferenceChanged(
+                SharedPreferences prefs, String key) {
+            if (BrowserBookmarksPage.PREF_ACCOUNT_TYPE.equals(key)) {
+                mAccountType = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, null);
+                mBreadcrumbs.clear();
+                loadData();
+            }
+            if (BrowserBookmarksPage.PREF_ACCOUNT_NAME.equals(key)) {
+                mAccountName = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, null);
+                mBreadcrumbs.clear();
+                loadData();
+            }
+        }
     }
 
     // Class containing the rendering information for a specific bookmark.
diff --git a/tests/assets/popular_urls.txt b/tests/assets/popular_urls.txt
new file mode 100644
index 0000000..70d0997
--- /dev/null
+++ b/tests/assets/popular_urls.txt
@@ -0,0 +1,4 @@
+http://google.com
+http://nytimes.com
+http://slashdot.org
+
diff --git a/tests/src/com/android/browser/JNIBindingsTestApp.java b/tests/src/com/android/browser/JNIBindingsTestApp.java
index 4c8802a..f4efa2c 100644
--- a/tests/src/com/android/browser/JNIBindingsTestApp.java
+++ b/tests/src/com/android/browser/JNIBindingsTestApp.java
@@ -50,6 +50,7 @@
     private static final int MSG_WEBKIT_DATA_READY = 101;
 
     private BrowserActivity mActivity = null;
+    private Controller mController = null;
     private Instrumentation mInst = null;
 
     private boolean mTestDone = false;
@@ -111,6 +112,7 @@
         super.setUp();
 
         mActivity = getActivity();
+        mController = mActivity.getController();
         mInst = getInstrumentation();
         mInst.waitForIdleSync();
 
@@ -147,7 +149,7 @@
      * and wrapping the WebView's helper clients.
      */
     void setUpBrowser() {
-        Tab tab = mActivity.getTabControl().getCurrentTab();
+        Tab tab = mController.getTabControl().getCurrentTab();
         WebView webView = tab.getWebView();
         webView.addJavascriptInterface(new JNIBindingsTest(this), "JNIBindingsTest");
 
@@ -229,7 +231,7 @@
     public void testJNIBindings() {
         setUpBrowser();
 
-        Tab tab = mActivity.getTabControl().getCurrentTab();
+        Tab tab = mController.getTabControl().getCurrentTab();
         WebView webView = tab.getWebView();
         webView.loadUrl("file://" + SDCARD_BINDINGS_TEST_HTML);
         synchronized(this) {
diff --git a/tests/src/com/android/browser/PopularUrlsTest.java b/tests/src/com/android/browser/PopularUrlsTest.java
index 98a0e9f..ad1fe4f 100644
--- a/tests/src/com/android/browser/PopularUrlsTest.java
+++ b/tests/src/com/android/browser/PopularUrlsTest.java
@@ -17,11 +17,13 @@
 package com.android.browser;
 
 import android.app.Instrumentation;
+import android.content.Context;
 import android.content.Intent;
+import android.content.res.AssetManager;
 import android.net.Uri;
 import android.net.http.SslError;
-import android.os.Environment;
 import android.test.ActivityInstrumentationTestCase2;
+import android.text.TextUtils;
 import android.util.Log;
 import android.webkit.HttpAuthHandler;
 import android.webkit.JsPromptResult;
@@ -35,6 +37,7 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -53,13 +56,13 @@
     private final static String sInputFile = "popular_urls.txt";
     private final static String sOutputFile = "test_output.txt";
     private final static String sStatusFile = "test_status.txt";
-    private final static File sExternalStorage = Environment.getExternalStorageDirectory();
 
     private final static int PERF_LOOPCOUNT = 10;
     private final static int STABILITY_LOOPCOUNT = 1;
     private final static int PAGE_LOAD_TIMEOUT = 120000; // 2 minutes
 
     private BrowserActivity mActivity = null;
+    private Controller mController = null;
     private Instrumentation mInst = null;
     private CountDownLatch mLatch = new CountDownLatch(1);
     private RunStatus mStatus;
@@ -76,10 +79,11 @@
         Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("about:blank"));
         setActivityIntent(i);
         mActivity = getActivity();
+        mController = mActivity.getController();
         mInst = getInstrumentation();
         mInst.waitForIdleSync();
 
-        mStatus = RunStatus.load();
+        mStatus = RunStatus.load(getInstrumentation().getContext());
     }
 
     @Override
@@ -91,16 +95,10 @@
         super.tearDown();
     }
 
-    static BufferedReader getInputStream() throws FileNotFoundException {
-        return getInputStream(sInputFile);
-    }
-
-    static BufferedReader getInputStream(String inputFile) throws FileNotFoundException {
-        String path = sExternalStorage + File.separator + inputFile;
-        FileReader fileReader = new FileReader(path);
-        BufferedReader bufferedReader = new BufferedReader(fileReader);
-
-        return bufferedReader;
+    BufferedReader getInputStream() throws IOException {
+        AssetManager assets = getInstrumentation().getContext().getAssets();
+        return new BufferedReader(
+            new InputStreamReader(assets.open(sInputFile)));
     }
 
     OutputStreamWriter getOutputStream() throws IOException {
@@ -108,10 +106,8 @@
     }
 
     OutputStreamWriter getOutputStream(String outputFile) throws IOException {
-        String path = sExternalStorage + File.separator + outputFile;
-
-        File file = new File(path);
-
+        File file = new File(getInstrumentation().getContext()
+                .getExternalFilesDir(null), outputFile);
         return new FileWriter(file, mStatus.getIsRecovery());
     }
 
@@ -120,7 +116,16 @@
      * and wrapping the WebView's helper clients.
      */
     void setUpBrowser() {
-        Tab tab = mActivity.getTabControl().getCurrentTab();
+        mInst.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                setupBrowserInternal();
+            }
+        });
+    }
+
+    void setupBrowserInternal() {
+        Tab tab = mController.getTabControl().getCurrentTab();
         WebView webView = tab.getWebView();
 
         webView.setWebChromeClient(new TestWebChromeClient(webView.getWebChromeClient()) {
@@ -219,6 +224,7 @@
              */
             @Override
             public void onPageFinished(WebView view, String url) {
+                super.onPageFinished(view, url);
                 if (!pageLoadFinishCalled) {
                     pageLoadFinishCalled = true;
                     if (pageProgressFull) {
@@ -252,7 +258,7 @@
             // try to stop page load
             mInst.runOnMainSync(new Runnable(){
                 public void run() {
-                    mActivity.getTabControl().getCurrentTab().getWebView().stopLoading();
+                    mController.getTabControl().getCurrentTab().getWebView().stopLoading();
                 }
             });
             // try to wait for count down latch again
@@ -270,8 +276,8 @@
         private String url;
         private boolean isRecovery;
 
-        private RunStatus(String file) throws IOException {
-            mFile = new File(file);
+        private RunStatus(File file) throws IOException {
+            mFile = file;
             FileReader input = null;
             BufferedReader reader = null;
             isRecovery = false;
@@ -307,12 +313,13 @@
             }
         }
 
-        public static RunStatus load() throws IOException {
-            return load(sStatusFile);
+        public static RunStatus load(Context context) throws IOException {
+            return load(context, sStatusFile);
         }
 
-        public static RunStatus load(String file) throws IOException {
-            return new RunStatus(sExternalStorage + File.separator + file);
+        public static RunStatus load(Context context, String file) throws IOException {
+            return new RunStatus(new File(
+                    context.getExternalFilesDir(null), file));
         }
 
         public void write() throws IOException {
@@ -380,14 +387,16 @@
     void loopUrls(BufferedReader input, OutputStreamWriter writer,
             boolean clearCache, int loopCount)
             throws IOException, InterruptedException {
-        Tab tab = mActivity.getTabControl().getCurrentTab();
+        Tab tab = mController.getTabControl().getCurrentTab();
         WebView webView = tab.getWebView();
 
         List<String> pages = new LinkedList<String>();
 
         String page;
         while (null != (page = input.readLine())) {
-            pages.add(page);
+            if (!TextUtils.isEmpty(page)) {
+                pages.add(page);
+            }
         }
 
         Iterator<String> iterator = pages.iterator();