Merge change 646

* changes:
  Use ViewStub for setEmptyView (bug 1803058)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c67cc97..ace5750 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -176,6 +176,19 @@
                   android:theme="@android:style/Theme.Dialog">
         </activity>
 
+        <activity android:name="BookmarkSearch"
+                  android:label="@string/bookmarks_search"
+                  android:stateNotNeeded="true"
+                  android:theme="@android:style/Theme.NoDisplay"
+                  android:excludeFromRecents="true">
+            <intent-filter>
+                <action android:name="android.intent.action.SEARCH" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="android.app.searchable"
+                    android:resource="@xml/bookmarks_searchable" />
+        </activity>
+
         <service android:name="GearsDialogService"
                  android:process=":dialog"
                  android:exported="false">
diff --git a/res/drawable/ic_search_category_bookmark.png b/res/drawable/ic_search_category_bookmark.png
index 08b5e74..45755ff 100755
--- a/res/drawable/ic_search_category_bookmark.png
+++ b/res/drawable/ic_search_category_bookmark.png
Binary files differ
diff --git a/res/drawable/ic_search_category_history.png b/res/drawable/ic_search_category_history.png
index 894c254..e5c45f7 100755
--- a/res/drawable/ic_search_category_history.png
+++ b/res/drawable/ic_search_category_history.png
Binary files differ
diff --git a/res/drawable/ic_search_category_suggest.png b/res/drawable/ic_search_category_suggest.png
index ada07e6..a4ed7aa 100755
--- a/res/drawable/ic_search_category_suggest.png
+++ b/res/drawable/ic_search_category_suggest.png
Binary files differ
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index fd9e65c..95dcec0 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -18,7 +18,7 @@
     <string name="application_name">"瀏覽器"</string>
     <string name="tab_bookmarks">"書籤"</string>
     <string name="tab_most_visited">"常用網頁"</string>
-    <string name="tab_history">"紀錄"</string>
+    <string name="tab_history">"記錄"</string>
     <string name="added_to_bookmarks">"已加入書籤"</string>
     <string name="removed_from_bookmarks">"已自書籤中移除"</string>
     <string name="sign_in_to">"登入到 <xliff:g id="HOSTNAME">%s1</xliff:g>「<xliff:g id="REALM">%s2</xliff:g>」"</string>
@@ -27,10 +27,12 @@
     <string name="action">"登入"</string>
     <string name="cancel">"取消"</string>
     <string name="ok">"確定"</string>
-    <!-- no translation found for matches_found:zero (6242659159545399963) -->
-    <!-- no translation found for matches_found:one (4352019729062956802) -->
-    <!-- no translation found for matches_found:few (5544267486978946555) -->
-    <!-- no translation found for matches_found:other (6616125067364315405) -->
+  <plurals name="matches_found">
+    <item quantity="zero">"沒有相配項"</item>
+    <item quantity="one">"1 個相配項"</item>
+    <item quantity="few">"<xliff:g id="NUMBER">%d</xliff:g> 個相配項"</item>
+    <item quantity="other">"<xliff:g id="NUMBER">%d</xliff:g> 個相配項"</item>
+  </plurals>
     <string name="page_info">"頁面資訊"</string>
     <string name="page_info_view">"檢視頁面資訊"</string>
     <string name="page_info_address">"位址:"</string>
@@ -67,16 +69,16 @@
     <string name="create_shortcut_bookmark">"新增捷徑至首頁"</string>
     <string name="open_bookmark">"開啟"</string>
     <string name="remove_bookmark">"刪除書籤"</string>
-    <string name="remove_history_item">"從紀錄中移除"</string>
-    <string name="bookmark_saved">"儲存至書籤。"</string>
-    <string name="bookmark_needs_title">"請輸入書籤名字。"</string>
+    <string name="remove_history_item">"從記錄中移除"</string>
+    <string name="bookmark_saved">"已儲存至書籤。"</string>
+    <string name="bookmark_needs_title">"請輸入書籤名稱。"</string>
     <string name="bookmark_needs_url">"請選取書籤位置。"</string>
     <string name="empty_bookmark">"無法建立空白書籤。"</string>
     <string name="bookmark_url_not_valid">"網址無效。"</string>
     <string name="delete_bookmark">"刪除"</string>
-    <string name="bookmark_page">"最後瀏覽的書籤頁面"</string>
+    <string name="bookmark_page">"將最後瀏覽的網頁加入書籤"</string>
     <string name="current_page">"來源 "</string>
-    <string name="delete_bookmark_warning">"<xliff:g id="BOOKMARK">%s</xliff:g>"</string>
+    <string name="delete_bookmark_warning">"刪除「<xliff:g id="BOOKMARK">%s</xliff:g>」書籤?"</string>
     <string name="open_in_new_window">"在新視窗開啟"</string>
     <string name="new_window">"新視窗"</string>
     <string name="goto_dot">"前往"</string>
@@ -88,16 +90,16 @@
     <string name="tab_picker_view_tab">"檢視"</string>
     <string name="tab_picker_new_tab">"新視窗"</string>
     <string name="tab_picker_remove_tab">"關閉"</string>
-    <string name="tab_picker_bookmark">"書籤"</string>
+    <string name="tab_picker_bookmark">"加入書籤"</string>
     <string name="tab_picker_send_url">"分享連結"</string>
     <string name="bookmarks">"書籤"</string>
     <string name="shortcut_bookmark">"書籤"</string>
-    <string name="history">"紀錄"</string>
+    <string name="history">"記錄"</string>
     <string name="menu_view_download">"下載"</string>
     <string name="share_page">"分享網頁"</string>
     <string name="contextmenu_openlink">"開啟"</string>
     <string name="contextmenu_openlink_newwindow">"在新視窗開啟"</string>
-    <string name="contextmenu_bookmark_thislink">"書籤連結"</string>
+    <string name="contextmenu_bookmark_thislink">"將連結加入書籤"</string>
     <string name="contextmenu_savelink">"儲存連結"</string>
     <string name="contextmenu_sharelink">"分享連結"</string>
     <string name="contextmenu_copy">"複製"</string>
@@ -105,7 +107,7 @@
     <string name="contextmenu_download_image">"儲存圖片"</string>
     <string name="contextmenu_view_image">"檢視圖片"</string>
     <string name="contextmenu_dial_dot">"撥打…"</string>
-    <string name="contextmenu_add_contact">"新增連絡人"</string>
+    <string name="contextmenu_add_contact">"新增聯絡人"</string>
     <string name="contextmenu_send_mail">"傳送電子郵件"</string>
     <string name="contextmenu_map">"地圖"</string>
     <string name="clear">"清除"</string>
@@ -114,14 +116,14 @@
     <string name="menu_preferences">"設定"</string>
     <string name="pref_content_title">"頁面內容設定"</string>
     <string name="pref_content_load_images">"載入圖片"</string>
-    <string name="pref_content_load_images_summary">"在網頁上顯示圖片"</string>
-    <string name="pref_content_block_popups">"阻擋快顯視窗"</string>
+    <string name="pref_content_load_images_summary">"顯示網頁上的圖片"</string>
+    <string name="pref_content_block_popups">"封鎖快顯視窗"</string>
     <string name="pref_content_javascript">"啟用 JavaScript"</string>
-    <string name="pref_content_open_in_background">"在背景開啟"</string>
-    <string name="pref_content_open_in_background_summary">"新分頁開在目前頁面後面"</string>
+    <string name="pref_content_open_in_background">"在背景中開啟"</string>
+    <string name="pref_content_open_in_background_summary">"在目前頁面後面開啟新分頁"</string>
     <string name="pref_content_homepage">"設定首頁"</string>
     <string name="pref_content_autofit">"自動調整頁面"</string>
-    <string name="pref_content_autofit_summary">"將網頁調整至適合螢幕大小"</string>
+    <string name="pref_content_autofit_summary">"將網頁調整至適合螢幕的大小"</string>
     <string name="pref_privacy_title">"隱私設定"</string>
     <string name="pref_privacy_clear_cache">"清除快取"</string>
     <string name="pref_privacy_clear_cache_summary">"刪除所有快取頁面內容"</string>
@@ -129,19 +131,19 @@
     <string name="pref_privacy_clear_cookies">"清除所有 cookie"</string>
     <string name="pref_privacy_clear_cookies_summary">"清除所有 cookie"</string>
     <string name="pref_privacy_clear_cookies_dlg">"所有 cookie 會被清除。"</string>
-    <string name="pref_privacy_clear_history">"清除紀錄"</string>
+    <string name="pref_privacy_clear_history">"清除記錄"</string>
     <string name="pref_privacy_clear_history_summary">"清除瀏覽記錄"</string>
-    <string name="pref_privacy_clear_history_dlg">"瀏覽記錄會被清除。"</string>
-    <string name="pref_privacy_clear_form_data">"清除表單資訊"</string>
+    <string name="pref_privacy_clear_history_dlg">"瀏覽記錄將被清除。"</string>
+    <string name="pref_privacy_clear_form_data">"清除表單資料"</string>
     <string name="pref_privacy_clear_form_data_summary">"清除所有表單"</string>
     <string name="pref_privacy_clear_form_data_dlg">"所有儲存的資料會被清除。"</string>
     <string name="pref_privacy_clear_passwords">"清除密碼"</string>
-    <string name="pref_privacy_clear_passwords_summary">"清除所有儲存密碼"</string>
+    <string name="pref_privacy_clear_passwords_summary">"清除所有儲存的密碼"</string>
     <string name="pref_privacy_clear_passwords_dlg">"所有儲存密碼會被清除"</string>
     <string name="pref_security_title">"安全性設定"</string>
     <string name="pref_security_remember_passwords">"記住密碼"</string>
     <string name="pref_security_remember_passwords_summary">"儲存網站使用者名稱與密碼"</string>
-    <string name="pref_security_save_form_data">"儲存表單資訊"</string>
+    <string name="pref_security_save_form_data">"儲存表單資料"</string>
     <string name="pref_security_save_form_data_summary">"儲存輸入的表單資料"</string>
     <string name="pref_security_show_security_warning">"顯示安全性警告"</string>
     <string name="pref_security_show_security_warning_summary">"當網站安全性有問題時,顯示警告訊息"</string>
@@ -149,25 +151,25 @@
     <string name="pref_security_accept_cookies_summary">"允許網站存取 cookie"</string>
     <string name="pref_text_size">"設定文字大小"</string>
   <string-array name="pref_text_size_choices">
-    <item>"極小"</item>
+    <item>"最小"</item>
     <item>"小"</item>
-    <item>"一般"</item>
+    <item>"適中"</item>
     <item>"大"</item>
-    <item>"特大"</item>
+    <item>"最大"</item>
   </string-array>
     <string name="pref_text_size_dialogtitle">"文字大小"</string>
     <string name="pref_extras_title">"進階設定"</string>
-    <string name="pref_extras_gears_enable">"啟用裝置"</string>
+    <string name="pref_extras_gears_enable">"啟用 Gears"</string>
     <string name="pref_extras_gears_enable_summary">"可擴充瀏覽器功能的應用程式"</string>
-    <string name="pref_extras_gears_settings">"裝置設定"</string>
+    <string name="pref_extras_gears_settings">"Gears 設定"</string>
     <string name="pref_plugin_installed">"外掛程式清單"</string>
     <string name="pref_plugin_installed_empty_list">"未安裝外掛程式。"</string>
     <string name="pref_extras_gears_settings_summary">"可擴充瀏覽器功能的應用程式"</string>
-    <string name="pref_extras_reset_default">"回復初始設定"</string>
-    <string name="pref_extras_reset_default_summary">"清除瀏覽器所有資料,並回復初始設定"</string>
-    <string name="pref_extras_reset_default_dlg">"瀏覽器所有資料會被清除, 並回復初始設定。"</string>
-    <string name="pref_extras_reset_default_dlg_title">"回復初始設定"</string>
-    <string name="pref_development_title">"除錯"</string>
+    <string name="pref_extras_reset_default">"還原至初始設定"</string>
+    <string name="pref_extras_reset_default_summary">"清除瀏覽器所有資料,並還原至初始設定"</string>
+    <string name="pref_extras_reset_default_dlg">"瀏覽器所有資料會被清除, 並還原至初始設定。"</string>
+    <string name="pref_extras_reset_default_dlg_title">"還原至初始設定"</string>
+    <string name="pref_development_title">"偵錯"</string>
     <string name="pref_default_text_encoding">"設定文字編碼"</string>
   <string-array name="pref_default_text_encoding_choices">
     <item>"Latin-1 (ISO-8859-1)"</item>
@@ -183,10 +185,10 @@
     <string name="browserFrameNetworkErrorLabel">"資料連線問題"</string>
     <string name="browserFrameFileErrorLabel">"檔案有問題"</string>
     <string name="browserFrameFormResubmitLabel">"確認"</string>
-    <string name="browserFrameFormResubmitMessage">"您正在瀏覽的頁面包含已傳送的 (「POSTDATA」),若重新送出資料,此頁表單中的資料會重複 (例如:搜尋結果、線上購物狀態)。"</string>
+    <string name="browserFrameFormResubmitMessage">"您想瀏覽的頁面包含已傳送的 (「POSTDATA」),如重新送出資料,此頁表單上執行的操作將會重複 (包括搜尋或線上購物等)。"</string>
     <string name="loadSuspendedTitle">"沒有網路連線"</string>
     <string name="loadSuspended">"連線恢復後,此頁面會繼續載入。"</string>
-    <string name="clear_history">"清除紀錄"</string>
+    <string name="clear_history">"清除記錄"</string>
     <string name="browser_history">"最近瀏覽的頁面"</string>
     <string name="empty_history">"瀏覽記錄是空的。"</string>
     <string name="add_new_bookmark">"加入書籤…"</string>
@@ -196,7 +198,7 @@
     <string name="attention">"注意"</string>
     <string name="popup_window_attempt">"此網站試圖開啟快顯視窗。"</string>
     <string name="allow">"允許"</string>
-    <string name="block">"阻擋"</string>
+    <string name="block">"封鎖"</string>
     <string name="too_many_windows_dialog_title">"已達視窗數量限制"</string>
     <string name="too_many_windows_dialog_message">"已達最大視窗數量限制,無法開啟新視窗。"</string>
     <string name="too_many_subwindows_dialog_title">"已開啟快顯視窗"</string>
@@ -218,7 +220,7 @@
     <string name="download_no_sdcard_dlg_title">"沒有 SD 卡"</string>
     <string name="download_no_sdcard_dlg_msg">"需要 SD 卡下載 <xliff:g id="FILENAME">%s</xliff:g>。"</string>
     <string name="download_sdcard_busy_dlg_title">"無法使用 SD 卡"</string>
-    <string name="download_sdcard_busy_dlg_msg">"SD 記憶卡忙碌中。若要允許下載,請選取通知區域中的 [停用 USB 儲存裝置]。"</string>
+    <string name="download_sdcard_busy_dlg_msg">"SD 記憶卡忙碌。如要允許下載,請選取通知中的 [停用 USB 儲存裝置]。"</string>
     <string name="download_no_application">"找不到可以開啟這個檔案的應用程式。"</string>
     <string name="retry">"重試"</string>
     <string name="no_downloads">"下載記錄是空的。"</string>
@@ -238,7 +240,7 @@
     <string name="permlab_readHistoryBookmarks">"讀取瀏覽器的瀏覽記錄和書籤"</string>
     <string name="permdesc_readHistoryBookmarks">"允許應用程式讀取瀏覽器造訪過的所有 URL 和瀏覽器所有的書籤。"</string>
     <string name="permlab_writeHistoryBookmarks">"寫入瀏覽器的瀏覽記錄和書籤"</string>
-    <string name="permdesc_writeHistoryBookmarks">"允許應用程式修改儲存在電話上的瀏覽器記錄或書籤。惡意應用程式可能會使用此選項來清除或修改您瀏覽器的資料。"</string>
+    <string name="permdesc_writeHistoryBookmarks">"允許應用程式修改儲存在電話上的瀏覽記錄或書籤。請注意:惡意應用程式可能會使用此選項來清除或修改您瀏覽器的資料。"</string>
     <string name="query_data_prompt">"允許儲存"</string>
     <string name="query_data_message">"此網站要在您的手機上儲存資訊。"</string>
     <string name="location_prompt">"存取您的位置資訊"</string>
@@ -248,7 +250,7 @@
     <string name="settings_message">"下表列出您為每個想要使用 Gears 之網站所授予的權限。"</string>
     <string name="filepicker_message">"檔案挑選程式"</string>
     <string name="image_message">"選擇的影像"</string>
-    <string name="settings_title">"裝置設定"</string>
+    <string name="settings_title">"Gears 設定"</string>
     <string name="privacy_policy">"讀取網站的隱私權政策,以瞭解網站將如何使用您的位置資訊。"</string>
     <string name="settings_storage_title">"允許本機儲存"</string>
     <string name="settings_storage_subtitle_on">"此網站可以在您的手機上儲存資訊"</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 82e4602..77464d7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -38,6 +38,8 @@
     <string name="password">Password</string>
     <!-- The label on the "sign in" button -->
     <string name="action">Sign in</string>
+    <!-- The name of the bookmarks and history search suggestion source.  -->
+    <string name="bookmarks_search">Bookmarks &amp; history</string>
 
     <!-- Label for a cancel button.  It is used for multiple cancel buttons in different contexts -->
     <string name="cancel">Cancel</string>
diff --git a/res/xml/bookmarks_searchable.xml b/res/xml/bookmarks_searchable.xml
new file mode 100644
index 0000000..bc6ac93
--- /dev/null
+++ b/res/xml/bookmarks_searchable.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+    android:label="@string/bookmarks_search"
+    android:searchButtonText="@string/search_button_text"
+    android:searchMode="queryRewriteFromData"
+    android:inputType="textUri"
+    android:imeOptions="actionGo"
+
+    android:includeInGlobalSearch="true"
+    android:searchSuggestAuthority="browser"
+    android:searchSuggestPath="bookmarks"
+    android:searchSuggestSelection="url LIKE ?"
+    android:searchSuggestIntentAction="android.intent.action.VIEW"
+/>
diff --git a/src/com/android/browser/BookmarkSearch.java b/src/com/android/browser/BookmarkSearch.java
new file mode 100644
index 0000000..4d3ca0f
--- /dev/null
+++ b/src/com/android/browser/BookmarkSearch.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 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.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * This activity is never started from the browser. Its purpose is to provide bookmark suggestions
+ * to global search (through its searchable meta-data), and to handle the intents produced
+ * by clicking such suggestions.
+ */
+public class BookmarkSearch extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = getIntent();
+        if (intent != null) {
+            String action = intent.getAction();
+            if (Intent.ACTION_VIEW.equals(action)) {
+                intent.setClass(this, BrowserActivity.class);
+                startActivity(intent);
+            }
+        }
+        finish();
+    }
+
+}
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index db4f634..bd68109 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -115,6 +115,7 @@
 import android.webkit.WebChromeClient;
 import android.webkit.WebHistoryItem;
 import android.webkit.WebIconDatabase;
+import android.webkit.WebStorage;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import android.widget.EditText;
@@ -2136,6 +2137,9 @@
                     .setVisibility(View.VISIBLE);
         }
         mContentView.removeView(mTabOverview);
+        // Clear all the data for tab picker so next time it will be
+        // recreated.
+        mTabControl.wipeAllPickerData();
         mTabOverview.clear();
         mTabOverview = null;
         mTabListener = null;
@@ -3429,6 +3433,34 @@
         public void onReceivedIcon(WebView view, Bitmap icon) {
             updateIcon(view.getUrl(), icon);
         }
+
+        /**
+         * The origin has exceeded it's database quota.
+         * @param url the URL that exceeded the quota
+         * @param databaseIdentifier the identifier of the database on
+         *     which the transaction that caused the quota overflow was run
+         * @param currentQuota the current quota for the origin.
+         * @param quotaUpdater The callback to run when a decision to allow or
+         *     deny quota has been made. Don't forget to call this!
+         */
+        @Override
+        public void onExceededDatabaseQuota(String url,
+            String databaseIdentifier, long currentQuota,
+            WebStorage.QuotaUpdater quotaUpdater) {
+            if(LOGV_ENABLED) {
+                Log.v(LOGTAG,
+                      "BrowserActivity received onExceededDatabaseQuota for "
+                      + url +
+                      ":"
+                      + databaseIdentifier +
+                      "(current quota: "
+                      + currentQuota +
+                      ")");
+            }
+            // Give the origin an extra megabyte to play with.
+            // TODO: This should show a prompt to the user, really :)
+            quotaUpdater.updateQuota(currentQuota + 1024 * 1024);
+        }
     };
 
     /**
@@ -4207,10 +4239,6 @@
                 }
             }
 
-            // Clear all the data for tab picker so next time it will be
-            // recreated.
-            mTabControl.wipeAllPickerData();
-
             // NEW_TAB means that the "New Tab" cell was clicked on.
             if (index == ImageGrid.NEW_TAB) {
                 openTabAndShow(mSettings.getHomePage(), null, false, null);
diff --git a/src/com/android/browser/BrowserProvider.java b/src/com/android/browser/BrowserProvider.java
index ce156a7..8c30873 100644
--- a/src/com/android/browser/BrowserProvider.java
+++ b/src/com/android/browser/BrowserProvider.java
@@ -17,14 +17,11 @@
 package com.android.browser;
 
 import com.google.android.providers.GoogleSettings.Partner;
-import java.util.Date;
 
-import android.app.ISearchManager;
 import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentUris;
-import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -33,17 +30,16 @@
 import android.content.SharedPreferences.Editor;
 import android.database.AbstractCursor;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
 import android.net.Uri;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Browser;
-import android.util.Log;
 import android.server.search.SearchableInfo;
 import android.text.util.Regex;
+import android.util.Log;
+
+import java.util.Date;
 
 
 public class BrowserProvider extends ContentProvider {
@@ -97,6 +93,7 @@
     private static final int URI_MATCH_SEARCHES_ID = 11;
     //
     private static final int URI_MATCH_SUGGEST = 20;
+    private static final int URI_MATCH_BOOKMARKS_SUGGEST = 21;
 
     private static final UriMatcher URI_MATCHER;
 
@@ -112,6 +109,9 @@
                 URI_MATCH_SEARCHES_ID);
         URI_MATCHER.addURI("browser", SearchManager.SUGGEST_URI_PATH_QUERY,
                 URI_MATCH_SUGGEST);
+        URI_MATCHER.addURI("browser",
+                TABLE_NAMES[URI_MATCH_BOOKMARKS] + "/" + SearchManager.SUGGEST_URI_PATH_QUERY,
+                URI_MATCH_BOOKMARKS_SUGGEST);
     }
 
     // 1 -> 2 add cache table
@@ -473,7 +473,7 @@
             throw new IllegalArgumentException("Unknown URL");
         }
 
-        if (match == URI_MATCH_SUGGEST) {
+        if (match == URI_MATCH_SUGGEST || match == URI_MATCH_BOOKMARKS_SUGGEST) {
             String suggestSelection;
             String [] myArgs;
             if (selectionArgs[0] == null || selectionArgs[0].equals("")) {
@@ -501,49 +501,22 @@
                     ORDER_BY,
                     (new Integer(MAX_SUGGESTION_LONG_ENTRIES)).toString());
 
-            if (Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) {
+            if (match == URI_MATCH_BOOKMARKS_SUGGEST
+                    || Regex.WEB_URL_PATTERN.matcher(selectionArgs[0]).matches()) {
                 return new MySuggestionCursor(c, null, "");
             } else {
                 // get Google suggest if there is still space in the list
                 if (myArgs != null && myArgs.length > 1
                         && c.getCount() < (MAX_SUGGESTION_SHORT_ENTRIES - 1)) {
-                    ISearchManager sm = ISearchManager.Stub
-                            .asInterface(ServiceManager
-                                    .getService(Context.SEARCH_SERVICE));
-                    SearchableInfo si = null;
-                    try {
-                        // use the global search to get Google suggest provider
-                        si = sm.getSearchableInfo(new ComponentName(
-                                getContext(), "com.android.browser"), true);
-
-                        // similar to the getSuggestions() in SearchDialog.java
-                        StringBuilder uriStr = new StringBuilder("content://");
-                        uriStr.append(si.getSuggestAuthority());
-                        // if content path provided, insert it now
-                        final String contentPath = si.getSuggestPath();
-                        if (contentPath != null) {
-                            uriStr.append('/');
-                            uriStr.append(contentPath);
-                        }
-                        // append standard suggestion query path 
-                        uriStr.append('/' + SearchManager.SUGGEST_URI_PATH_QUERY);
-                        // inject query, either as selection args or inline
-                        String[] selArgs = null;
-                        if (si.getSuggestSelection() != null) {
-                            selArgs = new String[] {selectionArgs[0]};
-                        } else {
-                            uriStr.append('/');
-                            uriStr.append(Uri.encode(selectionArgs[0]));
-                        }
-
-                        // finally, make the query
-                        Cursor sc = getContext().getContentResolver().query(
-                                Uri.parse(uriStr.toString()), null,
-                                si.getSuggestSelection(), selArgs, null);
-
-                        return new MySuggestionCursor(c, sc, selectionArgs[0]);
-                    } catch (RemoteException e) {
-                    }
+                    // TODO: This shouldn't be hard-coded. Instead, it should use the
+                    // default web search provider. But the API for that is not implemented yet.
+                    ComponentName googleSearchComponent = 
+                            new ComponentName("com.android.googlesearch", 
+                                    "com.android.googlesearch.GoogleSearch");
+                    SearchableInfo si = 
+                            SearchManager.getSearchableInfo(googleSearchComponent, false);
+                    Cursor sc = SearchManager.getSuggestions(getContext(), si, selectionArgs[0]);
+                    return new MySuggestionCursor(c, sc, selectionArgs[0]);
                 }
                 return new MySuggestionCursor(c, null, selectionArgs[0]);
             }
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index e47ed60..95ed17b 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -74,6 +74,8 @@
     private boolean autoFitPage = true;
     private boolean landscapeOnly = false;
     private boolean showDebugSettings = false;
+    private String databasePath; // default value set in loadFromDb()
+    private boolean databaseEnabled = true;
     // The Browser always enables Application Caches.
     private boolean appCacheEnabled = true;
     private String appCachePath;  // default value set in loadFromDb().
@@ -185,6 +187,10 @@
             s.setSupportMultipleWindows(true);
             // Turn off file access
             s.setAllowFileAccess(false);
+
+            s.setDatabasePath(b.databasePath);
+            s.setDatabaseEnabled(b.databaseEnabled);
+
             // Turn on Application Caches.
             s.setAppCachePath(b.appCachePath);
             s.setAppCacheEnabled(b.appCacheEnabled);
@@ -209,6 +215,8 @@
         pluginsPath = ctx.getDir("plugins", 0).getPath();
         // Set the default value for the Application Caches path.
         appCachePath = ctx.getDir("appcache", 0).getPath();
+        // Set the default value for the Database path.
+        databasePath = ctx.getDir("databases", 0).getPath();
 
         homeUrl = DEFAULT_HOME_URL +
                 Partner.getString(ctx.getContentResolver(), Partner.CLIENT_ID);
@@ -232,6 +240,8 @@
         pluginsEnabled = p.getBoolean("enable_plugins",
                 pluginsEnabled);
         pluginsPath = p.getString("plugins_path", pluginsPath);
+        databasePath = p.getString("database_path", databasePath);
+        databaseEnabled = p.getBoolean("enable_database", databaseEnabled);
         appCacheEnabled = p.getBoolean("enable_appcache",
             appCacheEnabled);
         appCachePath = p.getString("appcache_path", appCachePath);
diff --git a/src/com/android/browser/FakeWebView.java b/src/com/android/browser/FakeWebView.java
index 633b799..da5ef5f 100644
--- a/src/com/android/browser/FakeWebView.java
+++ b/src/com/android/browser/FakeWebView.java
@@ -33,17 +33,9 @@
  *  overrides ImageView so it can be used for the new tab image as well.
  */
 public class FakeWebView extends ImageView {
-    private TabControl.Tab mTab;
-    private Picture        mPicture;
+    private TabControl.PickerData mPickerData;
     private boolean        mUsesResource;
 
-    private class Listener implements WebView.PictureListener {
-        public void onNewPicture(WebView view, Picture p) {
-            FakeWebView.this.mPicture = p;
-            FakeWebView.this.invalidate();
-        }
-    };
-
     public FakeWebView(Context context) {
         this(context, null);
     }
@@ -68,17 +60,21 @@
             // would be nice to know if the picture is empty so we can avoid
             // drawing white.
             canvas.drawColor(Color.WHITE);
-            if (mTab != null) {
-                final WebView w = mTab.getTopWindow();
-                if (w != null) {
-                    if (mPicture != null) {
-                        canvas.save();
-                        float scale = getWidth() * w.getScale() / w.getWidth();
-                        canvas.scale(scale, scale);
-                        canvas.translate(-w.getScrollX(), -w.getScrollY());
-                        canvas.drawPicture(mPicture);
-                        canvas.restore();
+            if (mPickerData != null) {
+                final Picture p = mPickerData.mPicture;
+                if (p != null) {
+                    canvas.save();
+                    float scale = getWidth() * mPickerData.mScale
+                            / mPickerData.mWidth;
+                    // Check for NaN and infinity.
+                    if (Float.isNaN(scale) || Float.isInfinite(scale)) {
+                        scale = 1.0f;
                     }
+                    canvas.scale(scale, scale);
+                    canvas.translate(-mPickerData.mScrollX,
+                            -mPickerData.mScrollY);
+                    canvas.drawPicture(p);
+                    canvas.restore();
                 }
             }
         }
@@ -87,25 +83,24 @@
     @Override
     public void setImageResource(int resId) {
         mUsesResource = true;
-        mTab = null;
+        mPickerData = null;
         super.setImageResource(resId);
     }
 
     /**
      *  Set a WebView for this FakeWebView to represent.
-     *  @param  v WebView whose picture and other data will be used in onDraw.
+     *  @param  t The tab whose picture and other data will be used in onDraw.
      */
     public void setTab(TabControl.Tab t) {
         mUsesResource = false;
-        mTab = t;
-        if (t != null && t.getWebView() != null) {
-            Listener l = new Listener();
-            if (t.getSubWebView() != null) {
-                t.getSubWebView().setPictureListener(l);
-            } else {
-                t.getWebView().setPictureListener(l);
-            }
-            mPicture = mTab.getTopWindow().capturePicture();
+        if (mPickerData != null) {
+            // Clear the old tab's view first
+            mPickerData.mFakeWebView = null;
+        }
+        mPickerData = null;
+        if (t != null && t.getPickerData() != null) {
+            mPickerData = t.getPickerData();
+            mPickerData.mFakeWebView = this;
         }
     }
 }
diff --git a/src/com/android/browser/ImageAdapter.java b/src/com/android/browser/ImageAdapter.java
index e957143..f95753a 100644
--- a/src/com/android/browser/ImageAdapter.java
+++ b/src/com/android/browser/ImageAdapter.java
@@ -27,7 +27,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.LayoutInflater;
-import android.webkit.WebView;
 import android.widget.ImageView;
 import android.widget.ListAdapter;
 import android.widget.TextView;
@@ -73,22 +72,10 @@
      * Clear the internal WebViews and remove their picture listeners.
      */
     public void clear() {
-        for (TabControl.Tab t : mItems) {
-            clearPictureListeners(t);
-        }
         mItems.clear();
         notifyObservers();
     }
 
-    private void clearPictureListeners(TabControl.Tab t) {
-        if (t.getWebView() != null) {
-            t.getWebView().setPictureListener(null);
-            if (t.getSubWebView() != null) {
-                t.getSubWebView().setPictureListener(null);
-            }
-        }
-    }
-
     /**
      * Add a new window web page to the grid
      * 
@@ -113,7 +100,7 @@
      */
     public void remove(int index) {
         if (index >= 0 && index < mItems.size()) {
-            clearPictureListeners(mItems.remove(index));
+            mItems.remove(index);
             notifyObservers();
             mMaxedOut = false;
         }
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index cfe63a6..9de0198 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -17,6 +17,7 @@
 package com.android.browser;
 
 import android.content.Context;
+import android.graphics.Picture;
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.os.Message;
@@ -39,6 +40,7 @@
 import android.widget.ImageButton;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.util.ArrayList;
 import java.util.Vector;
 
@@ -142,10 +144,24 @@
         }
     }
 
+    // Extra saved information for displaying the tab in the picker.
+    public static class PickerData {
+        String  mUrl;
+        String  mTitle;
+        float   mScale;
+        int     mScrollX;
+        int     mScrollY;
+        int     mWidth;
+        Picture mPicture;
+        // This can be null. When a new picture comes in, this view should be
+        // invalidated to show the new picture.
+        FakeWebView mFakeWebView;
+    }
+
     /**
      * Private class for maintaining Tabs with a main WebView and a subwindow.
      */
-    public class Tab {
+    public class Tab implements WebView.PictureListener {
         // Main WebView
         private WebView mMainView;
         // Subwindow WebView
@@ -160,10 +176,9 @@
         // information needed to restore the WebView if the user goes back to
         // the tab.
         private Bundle mSavedState;
-        // Extra saved information for displaying the tab in the picker.
-        private String mUrl;
-        private String mTitle;
- 
+        // Data used when displaying the tab in the picker.
+        private PickerData mPickerData;
+
         // Parent Tab. This is the Tab that created this Tab, or null
         // if the Tab was created by the UI
         private Tab mParentTab;
@@ -234,7 +249,10 @@
          * @return The WebView's url or null.
          */
         public String getUrl() {
-            return mUrl;
+            if (mPickerData != null) {
+                return mPickerData.mUrl;
+            }
+            return null;
         }
 
         /**
@@ -245,7 +263,17 @@
          * @return The WebView's title (or url) or null.
          */
         public String getTitle() {
-            return mTitle;
+            if (mPickerData != null) {
+                return mPickerData.mTitle;
+            }
+            return null;
+        }
+
+        /**
+         * Returns the picker data.
+         */
+        public PickerData getPickerData() {
+            return mPickerData;
         }
 
         private void setParentTab(Tab parent) {
@@ -307,6 +335,18 @@
         public boolean closeOnExit() {
             return mCloseOnExit;
         }
+
+        public void onNewPicture(WebView view, Picture p) {
+            if (mPickerData == null) {
+                return;
+            }
+
+            mPickerData.mPicture = p;
+            // Tell the FakeWebView to redraw.
+            if (mPickerData.mFakeWebView != null) {
+                mPickerData.mFakeWebView.invalidate();
+            }
+        }
     };
 
     // Directory to store thumbnails for each WebView.
@@ -486,8 +526,8 @@
         // This tab may have been pushed in to the background and then closed.
         // If the saved state contains a picture file, delete the file.
         if (t.mSavedState != null) {
-            if (t.mSavedState.containsKey("picture")) {
-                new File(t.mSavedState.getString("picture")).delete();
+            if (t.mSavedState.containsKey(CURRPICTURE)) {
+                new File(t.mSavedState.getString(CURRPICTURE)).delete();
             }
         }
 
@@ -546,6 +586,8 @@
     private static final String CURRTAB = "currentTab";
     private static final String CURRURL = "currentUrl";
     private static final String CURRTITLE = "currentTitle";
+    private static final String CURRWIDTH = "currentWidth";
+    private static final String CURRPICTURE = "currentPicture";
     private static final String CLOSEONEXIT = "closeonexit";
     private static final String PARENTTAB = "parentTab";
     private static final String APPID = "appid";
@@ -598,8 +640,7 @@
                     Tab t = new Tab(null, false, null, null);
                     t.mSavedState = inState.getBundle(WEBVIEW + i);
                     if (t.mSavedState != null) {
-                        t.mUrl = t.mSavedState.getString(CURRURL);
-                        t.mTitle = t.mSavedState.getString(CURRTITLE);
+                        populatePickerDataFromSavedState(t);
                         // Need to maintain the app id and original url so we
                         // can possibly reuse this tab.
                         t.mAppId = t.mSavedState.getString(APPID);
@@ -795,8 +836,7 @@
         // Clear the saved state except for the app id and close-on-exit
         // values.
         t.mSavedState = null;
-        t.mUrl = null;
-        t.mTitle = null;
+        t.mPickerData = null;
         // Save the new url in order to avoid deleting the WebView.
         t.mOriginalUrl = url;
         return true;
@@ -921,30 +961,89 @@
     }
 
     /**
-     * Ensure that Tab t has a title, url, and favicon.
+     * Ensure that Tab t has data to display in the tab picker.
      * @param  t   Tab to populate.
      */
     /* package */ void populatePickerData(Tab t) {
-        if (t == null || t.mMainView == null) {
+        if (t == null) {
             return;
         }
+
+        // mMainView == null indicates that the tab has been freed.
+        if (t.mMainView == null) {
+            populatePickerDataFromSavedState(t);
+            return;
+        }
+
         // FIXME: The only place we cared about subwindow was for 
         // bookmarking (i.e. not when saving state). Was this deliberate?
         final WebBackForwardList list = t.mMainView.copyBackForwardList();
         final WebHistoryItem item =
                 list != null ? list.getCurrentItem() : null;
         populatePickerData(t, item);
+
+        // This method is only called during the tab picker creation. At this
+        // point we need to listen for new pictures since the WebView is still
+        // active.
+        final WebView w = t.getTopWindow();
+        w.setPictureListener(t);
+        // Capture the picture here instead of populatePickerData since it can
+        // be called when saving the state of a tab.
+        t.mPickerData.mPicture = w.capturePicture();
     }
 
-    // Populate the picker data
-    private void populatePickerData(Tab t, WebHistoryItem item) {
-        if (item != null) {
-            t.mUrl = item.getUrl();
-            t.mTitle = item.getTitle();
-            if (t.mTitle == null) {
-                t.mTitle = t.mUrl;
+    // Create the PickerData and populate it using the saved state of the tab.
+    private void populatePickerDataFromSavedState(Tab t) {
+        if (t.mSavedState == null) {
+            return;
+        }
+
+        final PickerData data = new PickerData();
+        final Bundle state = t.mSavedState;
+        data.mUrl = state.getString(CURRURL);
+        data.mTitle = state.getString(CURRTITLE);
+        data.mWidth = state.getInt(CURRWIDTH, 0);
+        // XXX: These keys are from WebView.savePicture so if they change, this
+        // will break.
+        data.mScale = state.getFloat("scale", 1.0f);
+        data.mScrollX = state.getInt("scrollX", 0);
+        data.mScrollY = state.getInt("scrollY", 0);
+
+        if (state.containsKey(CURRPICTURE)) {
+            final File f = new File(t.mSavedState.getString(CURRPICTURE));
+            try {
+                final FileInputStream in = new FileInputStream(f);
+                data.mPicture = Picture.createFromStream(in);
+                in.close();
+            } catch (Exception ex) {
+                // Ignore any problems with inflating the picture. We just
+                // won't draw anything.
             }
         }
+
+        // Set the tab's picker data.
+        t.mPickerData = data;
+    }
+
+    // Populate the picker data using the given history item and the current
+    // top WebView.
+    private void populatePickerData(Tab t, WebHistoryItem item) {
+        final PickerData data = new PickerData();
+        if (item != null) {
+            data.mUrl = item.getUrl();
+            data.mTitle = item.getTitle();
+            if (data.mTitle == null) {
+                data.mTitle = data.mUrl;
+            }
+        }
+        // We want to display the top window in the tab picker but use the url
+        // and title of the main window.
+        final WebView w = t.getTopWindow();
+        data.mWidth = w.getWidth();
+        data.mScale = w.getScale();
+        data.mScrollX = w.getScrollX();
+        data.mScrollY = w.getScrollY();
+        t.mPickerData = data;
     }
     
     /**
@@ -955,8 +1054,14 @@
         for (int i = 0; i < size; i++) {
             final Tab t = getTab(i);
             if (t != null && t.mSavedState == null) {
-                t.mUrl = null;
-                t.mTitle = null;
+                t.mPickerData = null;
+            }
+            if (t.mMainView != null) {
+                // Clear the picture listeners.
+                t.mMainView.setPictureListener(null);
+                if (t.mSubView != null) {
+                    t.mSubView.setPictureListener(null);
+                }
             }
         }
     }
@@ -978,7 +1083,7 @@
                 final File f = new File(mThumbnailDir, w.hashCode()
                         + "_pic.save");
                 if (w.savePicture(b, f)) {
-                    b.putString("picture", f.getPath());
+                    b.putString(CURRPICTURE, f.getPath());
                 }
             }
 
@@ -986,12 +1091,17 @@
             final WebHistoryItem item =
                     list != null ? list.getCurrentItem() : null;
             populatePickerData(t, item);
-            if (t.mUrl != null) {
-                b.putString(CURRURL, t.mUrl);
+
+            // XXX: WebView.savePicture stores the scale and scroll positions
+            // in the bundle so we don't have to do it here.
+            final PickerData data = t.mPickerData;
+            if (data.mUrl != null) {
+                b.putString(CURRURL, data.mUrl);
             }
-            if (t.mTitle != null) {
-                b.putString(CURRTITLE, t.mTitle);
+            if (data.mTitle != null) {
+                b.putString(CURRTITLE, data.mTitle);
             }
+            b.putInt(CURRWIDTH, data.mWidth);
             b.putBoolean(CLOSEONEXIT, t.mCloseOnExit);
             if (t.mAppId != null) {
                 b.putString(APPID, t.mAppId);
@@ -1022,8 +1132,7 @@
         // Restore the internal state even if the WebView fails to restore.
         // This will maintain the app id, original url and close-on-exit values.
         t.mSavedState = null;
-        t.mUrl = null;
-        t.mTitle = null;
+        t.mPickerData = null;
         t.mCloseOnExit = b.getBoolean(CLOSEONEXIT);
         t.mAppId = b.getString(APPID);
         t.mOriginalUrl = b.getString(ORIGINALURL);
@@ -1033,8 +1142,8 @@
         if (list == null) {
             return false;
         }
-        if (b.containsKey("picture")) {
-            final File f = new File(b.getString("picture"));
+        if (b.containsKey(CURRPICTURE)) {
+            final File f = new File(b.getString(CURRPICTURE));
             w.restorePicture(b, f);
             f.delete();
         }