OSDN Git Service

resolved conflicts for merge of 95d601f6 to master
[android-x86/packages-apps-Browser.git] / src / com / android / browser / BrowserActivity.java
index 47f12e3..18fe793 100644 (file)
@@ -36,6 +36,7 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.DialogInterface.OnCancelListener;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.AssetManager;
@@ -57,6 +58,8 @@ import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.PaintDrawable;
 import android.hardware.SensorListener;
 import android.hardware.SensorManager;
+import android.location.Location;
+import android.location.LocationManager;
 import android.net.ConnectivityManager;
 import android.net.Uri;
 import android.net.WebAddress;
@@ -81,12 +84,12 @@ import android.provider.Browser;
 import android.provider.Contacts;
 import android.provider.Downloads;
 import android.provider.MediaStore;
+import android.provider.Settings;
 import android.provider.Contacts.Intents.Insert;
 import android.text.IClipboard;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.text.util.Regex;
-import android.util.Config;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.Gravity;
@@ -111,11 +114,13 @@ import android.webkit.CookieManager;
 import android.webkit.CookieSyncManager;
 import android.webkit.DownloadListener;
 import android.webkit.HttpAuthHandler;
+import android.webkit.PluginManager;
 import android.webkit.SslErrorHandler;
 import android.webkit.URLUtil;
 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;
@@ -151,11 +156,27 @@ public class BrowserActivity extends Activity
         View.OnCreateContextMenuListener,
         DownloadListener {
 
+    /* Define some aliases to make these debugging flags easier to refer to.
+     * This file imports android.provider.Browser, so we can't just refer to "Browser.DEBUG".
+     */
+    private final static boolean DEBUG = com.android.browser.Browser.DEBUG;
+    private final static boolean LOGV_ENABLED = com.android.browser.Browser.LOGV_ENABLED;
+    private final static boolean LOGD_ENABLED = com.android.browser.Browser.LOGD_ENABLED;
+
     private IGoogleLoginService mGls = null;
     private ServiceConnection mGlsConnection = null;
 
     private SensorManager mSensorManager = null;
 
+    private WebStorage.QuotaUpdater mWebStorageQuotaUpdater = null;
+
+    // These are single-character shortcuts for searching popular sources.
+    private static final int SHORTCUT_INVALID = 0;
+    private static final int SHORTCUT_GOOGLE_SEARCH = 1;
+    private static final int SHORTCUT_WIKIPEDIA_SEARCH = 2;
+    private static final int SHORTCUT_DICTIONARY_SEARCH = 3;
+    private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4;
+
     /* Whitelisted webpages
     private static HashSet<String> sWhiteList;
 
@@ -184,6 +205,9 @@ public class BrowserActivity extends Activity
                 try {
                     if (mGls == null) return;
 
+                    if (!homepage.startsWith("http://www.google.")) return;
+                    if (homepage.indexOf('?') == -1) return;
+
                     String hostedUser = mGls.getAccount(GoogleLoginServiceConstants.PREFER_HOSTED);
                     String googleUser = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE);
 
@@ -203,8 +227,7 @@ public class BrowserActivity extends Activity
 
                     if (googleUser == null || !hostedUser.equals(googleUser)) {
                         String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
-                        homepage = "http://www.google.com/m/a/" + domain + "?client=ms-" +
-                            Partner.getString(BrowserActivity.this.getContentResolver(), Partner.CLIENT_ID);
+                        homepage = homepage.replace("?", "/a/" + domain + "?");
                     }
                 } catch (RemoteException ignore) {
                     // Login service died; carry on
@@ -373,7 +396,7 @@ public class BrowserActivity extends Activity
                 s.loadFromDb(mContext);
                 pluginsPath = s.getPluginsPath();
             }
-            if (Config.LOGV) {
+            if (LOGV_ENABLED) {
                 Log.v(TAG, "Plugin path: " + pluginsPath);
             }
         }
@@ -400,7 +423,7 @@ public class BrowserActivity extends Activity
          * We delete the directory, then recreate it.
          */
         public void cleanPluginsDirectory() {
-          if (Config.LOGV) {
+          if (LOGV_ENABLED) {
             Log.v(TAG, "delete plugins directory: " + pluginsPath);
           }
           File pluginsDirectory = new File(pluginsPath);
@@ -416,7 +439,7 @@ public class BrowserActivity extends Activity
          */
         public void copyBuildInfos() {
           try {
-            if (Config.LOGV) {
+            if (LOGV_ENABLED) {
               Log.v(TAG, "Copy build infos to the plugins directory");
             }
             File buildInfoFile = new File(SYSTEM_BUILD_INFOS_FILE);
@@ -448,7 +471,7 @@ public class BrowserActivity extends Activity
             File buildInfoFile = new File(SYSTEM_BUILD_INFOS_FILE);
             File buildInfoPlugins = new File(pluginsPath, BUILD_INFOS_FILE);
             if (!buildInfoPlugins.exists()) {
-              if (Config.LOGV) {
+              if (LOGV_ENABLED) {
                 Log.v(TAG, "build.prop in plugins directory " + pluginsPath
                   + " does not exist, therefore it's a new system image");
               }
@@ -458,7 +481,7 @@ public class BrowserActivity extends Activity
               String buildInfoPlugin = contentsOfFile(buildInfoPlugins);
               if (buildInfo == null || buildInfoPlugin == null
                   || buildInfo.compareTo(buildInfoPlugin) != 0) {
-                if (Config.LOGV) {
+                if (LOGV_ENABLED) {
                   Log.v(TAG, "build.prop are different, "
                     + " therefore it's a new system image");
                 }
@@ -491,7 +514,7 @@ public class BrowserActivity extends Activity
               String path = entry.getName().substring(zipFilterLength);
               File outputFile = new File(pluginsPath, path);
               if (!outputFile.exists()) {
-                if (Config.LOGV) {
+                if (LOGV_ENABLED) {
                   Log.v(TAG, "checkIsDifferentVersions(): extracted file "
                     + path + " does not exist, we have a different version");
                 }
@@ -536,7 +559,7 @@ public class BrowserActivity extends Activity
                     outputFile.getParentFile().mkdirs();
 
                     if (outputFile.exists() && !mDoOverwrite) {
-                        if (Config.LOGV) {
+                        if (LOGV_ENABLED) {
                             Log.v(TAG, path + " already extracted.");
                         }
                     } else {
@@ -548,7 +571,7 @@ public class BrowserActivity extends Activity
                                 path + TEMPORARY_EXTENSION);
                         }
                         FileOutputStream fos = new FileOutputStream(outputFile);
-                        if (Config.LOGV) {
+                        if (LOGV_ENABLED) {
                             Log.v(TAG, "copy " + entry + " to "
                                 + pluginsPath + "/" + path);
                         }
@@ -563,7 +586,7 @@ public class BrowserActivity extends Activity
                     File renamedFile = (File) elems.nextElement();
                     File sourceFile = new File(renamedFile.getPath()
                         + TEMPORARY_EXTENSION);
-                    if (Config.LOGV) {
+                    if (LOGV_ENABLED) {
                         Log.v(TAG, "rename " + sourceFile.getPath()
                             + " to " + renamedFile.getPath());
                     }
@@ -571,11 +594,6 @@ public class BrowserActivity extends Activity
                 }
 
                 copyBuildInfos();
-
-                // Refresh the plugin list.
-                if (mTabControl.getCurrentWebView() != null) {
-                    mTabControl.getCurrentWebView().refreshPlugins(false);
-                }
             } catch (IOException e) {
                 Log.e(TAG, "IO Exception: " + e);
             }
@@ -622,16 +640,22 @@ public class BrowserActivity extends Activity
         }
     }
 
+    // Flag to enable the touchable browser bar with buttons
+    private final boolean CUSTOM_BROWSER_BAR = true;
+
     @Override public void onCreate(Bundle icicle) {
-        if (Config.LOGV) {
+        if (LOGV_ENABLED) {
             Log.v(LOGTAG, this + " onStart");
         }
         super.onCreate(icicle);
-        this.requestWindowFeature(Window.FEATURE_LEFT_ICON);
-        this.requestWindowFeature(Window.FEATURE_RIGHT_ICON);
-        this.requestWindowFeature(Window.FEATURE_PROGRESS);
-        this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
+        if (CUSTOM_BROWSER_BAR) {
+            this.requestWindowFeature(Window.FEATURE_NO_TITLE);
+        } else {
+            this.requestWindowFeature(Window.FEATURE_LEFT_ICON);
+            this.requestWindowFeature(Window.FEATURE_RIGHT_ICON);
+            this.requestWindowFeature(Window.FEATURE_PROGRESS);
+            this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+        }
         // test the browser in OpenGL
         // requestWindowFeature(Window.FEATURE_OPENGL);
 
@@ -659,8 +683,21 @@ public class BrowserActivity extends Activity
         mGenericFavicon = getResources().getDrawable(
                 R.drawable.app_web_browser_sm);
 
-        mContentView = (FrameLayout) getWindow().getDecorView().findViewById(
-                com.android.internal.R.id.content);
+        FrameLayout frameLayout = (FrameLayout) getWindow().getDecorView()
+                .findViewById(com.android.internal.R.id.content);
+        if (CUSTOM_BROWSER_BAR) {
+            // This LinearLayout will hold the title bar and a FrameLayout, which
+            // holds everything else.
+            LinearLayout linearLayout = (LinearLayout) LayoutInflater.from(this)
+                    .inflate(R.layout.custom_screen, null);
+            mTitleBar = (TitleBar) linearLayout.findViewById(R.id.title_bar);
+            mTitleBar.setBrowserActivity(this);
+            mContentView = (FrameLayout) linearLayout.findViewById(
+                    R.id.main_content);
+            frameLayout.addView(linearLayout, COVER_SCREEN_PARAMS);
+        } else {
+            mContentView = frameLayout;
+        }
 
         // Create the tab control and our initial tab
         mTabControl = new TabControl(this);
@@ -676,6 +713,12 @@ public class BrowserActivity extends Activity
         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");
 
+        // If this was a web search request, pass it on to the default web search provider.
+        if (handleWebSearchIntent(getIntent())) {
+            moveTaskToBack(true);
+            return;
+        }
+
         if (!mTabControl.restoreState(icicle)) {
             // clear up the thumbnail directory if we can't restore the state as
             // none of the files in the directory are referenced any more.
@@ -687,11 +730,12 @@ public class BrowserActivity extends Activity
             // If the intent is ACTION_VIEW and data is not null, the Browser is
             // invoked to view the content by another application. In this case,
             // the tab will be close when exit.
-            String url = getUrlFromIntent(intent);
+            UrlData urlData = getUrlDataFromIntent(intent);
+
             final TabControl.Tab t = mTabControl.createNewTab(
                     Intent.ACTION_VIEW.equals(intent.getAction()) &&
                     intent.getData() != null,
-                    intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), url);
+                    intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), urlData.mUrl);
             mTabControl.setCurrentTab(t);
             // This is one of the only places we call attachTabToContentView
             // without animating from the tab picker.
@@ -713,14 +757,19 @@ public class BrowserActivity extends Activity
             }
             copyPlugins(true);
 
-            if (url == null || url.length() == 0) {
+            if (urlData.isEmpty()) {
                 if (mSettings.isLoginInitialized()) {
                     webView.loadUrl(mSettings.getHomePage());
                 } else {
                     waitForCredentials();
                 }
             } else {
-                webView.loadUrl(url);
+                byte[] postData = getLocationData(intent);
+                if (postData != null) {
+                    webView.postUrl(urlData.mUrl, postData);
+                } else {
+                    urlData.loadIn(webView);
+                }
             }
         } else {
             // TabControl.restoreState() will create a new tab even if
@@ -745,6 +794,52 @@ public class BrowserActivity extends Activity
                     }
                 }
             };
+
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        mPackageInstallationReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                final String packageName = intent.getData()
+                        .getSchemeSpecificPart();
+                final boolean replacing = intent.getBooleanExtra(
+                        Intent.EXTRA_REPLACING, false);
+                if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
+                    // if it is replacing, refreshPlugins() when adding
+                    return;
+                }
+                PackageManager pm = BrowserActivity.this.getPackageManager();
+                PackageInfo pkgInfo = null;
+                try {
+                    pkgInfo = pm.getPackageInfo(packageName,
+                            PackageManager.GET_PERMISSIONS);
+                } catch (PackageManager.NameNotFoundException e) {
+                    return;
+                }
+                if (pkgInfo != null) {
+                    String permissions[] = pkgInfo.requestedPermissions;
+                    if (permissions == null) {
+                        return;
+                    }
+                    boolean permissionOk = false;
+                    for (String permit : permissions) {
+                        if (PluginManager.PLUGIN_PERMISSION.equals(permit)) {
+                            permissionOk = true;
+                            break;
+                        }
+                    }
+                    if (permissionOk) {
+                        PluginManager.getInstance(BrowserActivity.this)
+                                .refreshPlugins(
+                                        Intent.ACTION_PACKAGE_ADDED
+                                                .equals(action));
+                    }
+                }
+            }
+        };
+        registerReceiver(mPackageInstallationReceiver, filter);
     }
 
     @Override
@@ -774,10 +869,17 @@ public class BrowserActivity extends Activity
                 || Intent.ACTION_SEARCH.equals(action)
                 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
                 || Intent.ACTION_WEB_SEARCH.equals(action)) {
-            String url = getUrlFromIntent(intent);
-            if (url == null || url.length() == 0) {
-                url = mSettings.getHomePage();
+            // If this was a search request (e.g. search query directly typed into the address bar),
+            // pass it on to the default web search provider.
+            if (handleWebSearchIntent(intent)) {
+                return;
             }
+
+            UrlData urlData = getUrlDataFromIntent(intent);
+            if (urlData.isEmpty()) {
+                urlData = new UrlData(mSettings.getHomePage());
+            }
+
             if (Intent.ACTION_VIEW.equals(action) &&
                     (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
                 final String appId =
@@ -794,20 +896,21 @@ public class BrowserActivity extends Activity
                     // If the WebView has the same original url and is on that
                     // page, it can be reused.
                     boolean needsLoad =
-                            mTabControl.recreateWebView(appTab, url);
+                            mTabControl.recreateWebView(appTab, urlData.mUrl);
+                    
                     if (current != appTab) {
-                        showTab(appTab, needsLoad ? url : null);
+                        showTab(appTab, needsLoad ? urlData : EMPTY_URL_DATA);
                     } else {
                         if (mTabOverview != null && mAnimationCount == 0) {
                             sendAnimateFromOverview(appTab, false,
-                                    needsLoad ? url : null, TAB_OVERVIEW_DELAY,
-                                    null);
+                                    needsLoad ? urlData : EMPTY_URL_DATA, null,
+                                    TAB_OVERVIEW_DELAY, null);
                         } else {
                             // If the tab was the current tab, we have to attach
                             // it to the view system again.
                             attachTabToContentView(appTab);
                             if (needsLoad) {
-                                appTab.getWebView().loadUrl(url);
+                                urlData.loadIn(appTab.getWebView());
                             }
                         }
                     }
@@ -817,27 +920,98 @@ public class BrowserActivity extends Activity
                 // opened in a new tab unless we have reached MAX_TABS. Then the
                 // url will be opened in the current tab. If a new tab is
                 // created, it will have "true" for exit on close.
-                openTabAndShow(url, null, true, appId);
+                openTabAndShow(urlData, null, true, appId);
             } else {
-                if ("about:debug".equals(url)) {
+                if ("about:debug".equals(urlData.mUrl)) {
                     mSettings.toggleDebugSettings();
                     return;
                 }
+                byte[] postData = getLocationData(intent);
                 // If the Window overview is up and we are not in the midst of
                 // an animation, animate away from the Window overview.
                 if (mTabOverview != null && mAnimationCount == 0) {
-                    sendAnimateFromOverview(current, false, url,
-                            TAB_OVERVIEW_DELAY, null);
+                    sendAnimateFromOverview(current, false, urlData,
+                            postData, TAB_OVERVIEW_DELAY, null);
                 } else {
                     // Get rid of the subwindow if it exists
                     dismissSubWindow(current);
-                    current.getWebView().loadUrl(url);
+                    if (postData != null) {
+                        current.getWebView().postUrl(urlData.mUrl, postData);
+                    } else {
+                        urlData.loadIn(current.getWebView());
+                    }
                 }
             }
         }
     }
 
-    private String getUrlFromIntent(Intent intent) {
+    private int parseUrlShortcut(String url) {
+        if (url == null) return SHORTCUT_INVALID;
+
+        // FIXME: quick search, need to be customized by setting
+        if (url.length() > 2 && url.charAt(1) == ' ') {
+            switch (url.charAt(0)) {
+            case 'g': return SHORTCUT_GOOGLE_SEARCH;
+            case 'w': return SHORTCUT_WIKIPEDIA_SEARCH;
+            case 'd': return SHORTCUT_DICTIONARY_SEARCH;
+            case 'l': return SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH;
+            }
+        }
+        return SHORTCUT_INVALID;
+    }
+
+    /**
+     * Launches the default web search activity with the query parameters if the given intent's data
+     * are identified as plain search terms and not URLs/shortcuts.
+     * @return true if the intent was handled and web search activity was launched, false if not.
+     */
+    private boolean handleWebSearchIntent(Intent intent) {
+        if (intent == null) return false;
+
+        String url = null;
+        final String action = intent.getAction();
+        if (Intent.ACTION_VIEW.equals(action)) {
+            url = intent.getData().toString();
+        } else if (Intent.ACTION_SEARCH.equals(action)
+                || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
+                || Intent.ACTION_WEB_SEARCH.equals(action)) {
+            url = intent.getStringExtra(SearchManager.QUERY);
+        }
+        return handleWebSearchRequest(url);
+    }
+
+    /**
+     * Launches the default web search activity with the query parameters if the given url string
+     * was identified as plain search terms and not URL/shortcut.
+     * @return true if the request was handled and web search activity was launched, false if not.
+     */
+    private boolean handleWebSearchRequest(String inUrl) {
+        if (inUrl == null) return false;
+
+        // In general, we shouldn't modify URL from Intent.
+        // But currently, we get the user-typed URL from search box as well.
+        String url = fixUrl(inUrl).trim();
+
+        // URLs and site specific search shortcuts are handled by the regular flow of control, so
+        // return early.
+        if (Regex.WEB_URL_PATTERN.matcher(url).matches()
+                || ACCEPTED_URI_SCHEMA.matcher(url).matches()
+                || parseUrlShortcut(url) != SHORTCUT_INVALID) {
+            return false;
+        }
+
+        Browser.updateVisitedHistory(mResolver, url, false);
+        Browser.addSearchUrl(mResolver, url);
+
+        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.putExtra(SearchManager.QUERY, url);
+        startActivity(intent);
+
+        return true;
+    }
+
+    private UrlData getUrlDataFromIntent(Intent intent) {
         String url = null;
         if (intent != null) {
             final String action = intent.getAction();
@@ -850,6 +1024,13 @@ public class BrowserActivity extends Activity
                         url += "?" + mimeType;
                     }
                 }
+                if ("inline:".equals(url)) {
+                    return new InlinedUrlData(
+                            intent.getStringExtra(Browser.EXTRA_INLINE_CONTENT),
+                            intent.getType(),
+                            intent.getStringExtra(Browser.EXTRA_INLINE_ENCODING),
+                            intent.getStringExtra(Browser.EXTRA_INLINE_FAILURL));
+                }
             } else if (Intent.ACTION_SEARCH.equals(action)
                     || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
                     || Intent.ACTION_WEB_SEARCH.equals(action)) {
@@ -880,7 +1061,32 @@ public class BrowserActivity extends Activity
                 }
             }
         }
-        return url;
+        return new UrlData(url);
+    }
+
+    byte[] getLocationData(Intent intent) {
+        byte[] postData = null;
+        if (intent != null) {
+            final String action = intent.getAction();
+            if ((Intent.ACTION_SEARCH.equals(action)
+                    || Intent.ACTION_WEB_SEARCH.equals(action))
+                    && Settings.Secure.isLocationProviderEnabled(
+                            getContentResolver(),
+                            LocationManager.NETWORK_PROVIDER)) {
+                // Attempt to get location info
+                LocationManager locationManager = (LocationManager)
+                        getSystemService(Context.LOCATION_SERVICE);
+                Location location = locationManager
+                        .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+                if (location != null) {
+                    StringBuilder str = new StringBuilder("sll=");
+                    str.append(location.getLatitude()).append(",").append(
+                            location.getLongitude());
+                    postData = str.toString().getBytes();
+                }
+            }
+        }
+        return postData;
     }
 
     /* package */ static String fixUrl(String inUrl) {
@@ -990,7 +1196,7 @@ public class BrowserActivity extends Activity
 
     @Override protected void onResume() {
         super.onResume();
-        if (Config.LOGV) {
+        if (LOGV_ENABLED) {
             Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this);
         }
 
@@ -999,8 +1205,9 @@ public class BrowserActivity extends Activity
             return;
         }
 
+        mTabControl.resumeCurrentTab();
         mActivityInPause = false;
-        resumeWebView();
+        resumeWebViewTimers();
 
         if (mWakeLock.isHeld()) {
             mHandler.removeMessages(RELEASE_WAKELOCK);
@@ -1037,7 +1244,7 @@ public class BrowserActivity extends Activity
      *  the saved state.
      */
     @Override protected void onSaveInstanceState(Bundle outState) {
-        if (Config.LOGV) {
+        if (LOGV_ENABLED) {
             Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this);
         }
         // the default implementation requires each view to have an id. As the
@@ -1058,8 +1265,9 @@ public class BrowserActivity extends Activity
             return;
         }
 
+        mTabControl.pauseCurrentTab();
         mActivityInPause = true;
-        if (mTabControl.getCurrentIndex() >= 0 && !pauseWebView()) {
+        if (mTabControl.getCurrentIndex() >= 0 && !pauseWebViewTimers()) {
             mWakeLock.acquire();
             mHandler.sendMessageDelayed(mHandler
                     .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
@@ -1083,14 +1291,16 @@ public class BrowserActivity extends Activity
     }
 
     @Override protected void onDestroy() {
-        if (Config.LOGV) {
+        if (LOGV_ENABLED) {
             Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this);
         }
         super.onDestroy();
         // Remove the current tab and sub window
         TabControl.Tab t = mTabControl.getCurrentTab();
-        dismissSubWindow(t);
-        removeTabFromContentView(t);
+        if (t != null) {
+            dismissSubWindow(t);
+            removeTabFromContentView(t);
+        }
         // Destroy all the tabs
         mTabControl.destroy();
         WebIconDatabase.getInstance().close();
@@ -1108,6 +1318,8 @@ public class BrowserActivity extends Activity
         //        "com.android.masfproxyservice",
         //        "com.android.masfproxyservice.MasfProxyService"));
         //stopService(proxyServiceIntent);
+
+        unregisterReceiver(mPackageInstallationReceiver);
     }
 
     @Override
@@ -1156,7 +1368,7 @@ public class BrowserActivity extends Activity
         mTabControl.freeMemory();
     }
 
-    private boolean resumeWebView() {
+    private boolean resumeWebViewTimers() {
         if ((!mActivityInPause && !mPageStarted) ||
                 (mActivityInPause && mPageStarted)) {
             CookieSyncManager.getInstance().startSync();
@@ -1170,7 +1382,7 @@ public class BrowserActivity extends Activity
         }
     }
 
-    private boolean pauseWebView() {
+    private boolean pauseWebViewTimers() {
         if (mActivityInPause && !mPageStarted) {
             CookieSyncManager.getInstance().stopSync();
             WebView w = mTabControl.getCurrentWebView();
@@ -1294,6 +1506,9 @@ public class BrowserActivity extends Activity
         mCanChord = true;
         int id = item.getItemId();
         final WebView webView = getTopWindow();
+        if (null == webView) {
+            return false;
+        }
         final HashMap hrefMap = new HashMap();
         hrefMap.put("webview", webView);
         final Message msg = mHandler.obtainMessage(
@@ -1349,6 +1564,9 @@ public class BrowserActivity extends Activity
             // menu key.
             return false;
         }
+        if (null == mTabOverview && null == getTopWindow()) {
+            return false;
+        }
         switch (item.getItemId()) {
             // -- Main menu
             case R.id.goto_menu_id: {
@@ -1796,8 +2014,8 @@ public class BrowserActivity extends Activity
 
     // Send the ANIMTE_FROM_OVERVIEW message after changing the current tab.
     private void sendAnimateFromOverview(final TabControl.Tab tab,
-            final boolean newTab, final String url, final int delay,
-            final Message msg) {
+            final boolean newTab, final UrlData urlData, final byte[] postData,
+            final int delay, final Message msg) {
         // Set the current tab.
         mTabControl.setCurrentTab(tab);
         // Attach the WebView so it will layout.
@@ -1820,9 +2038,13 @@ public class BrowserActivity extends Activity
         // Load the url after the AnimatingView has captured the picture. This
         // prevents any bad layout or bad scale from being used during
         // animation.
-        if (url != null) {
+        if (!urlData.isEmpty()) {
             dismissSubWindow(tab);
-            tab.getWebView().loadUrl(url);
+            if (postData != null) {
+                tab.getWebView().postUrl(urlData.mUrl, postData);
+            } else {
+                urlData.loadIn(tab.getWebView());
+            }
         }
         map.put("msg", msg);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(
@@ -1838,15 +2060,15 @@ public class BrowserActivity extends Activity
     }
 
     // 500ms animation with 800ms delay
-    private static final int TAB_ANIMATION_DURATION = 500;
-    private static final int TAB_OVERVIEW_DELAY     = 800;
+    private static final int TAB_ANIMATION_DURATION = 200;
+    private static final int TAB_OVERVIEW_DELAY     = 500;
 
     // Called by TabControl when a tab is requesting focus
     /* package */ void showTab(TabControl.Tab t) {
-        showTab(t, null);
+        showTab(t, EMPTY_URL_DATA);
     }
 
-    private void showTab(TabControl.Tab t, String url) {
+    private void showTab(TabControl.Tab t, UrlData urlData) {
         // Disallow focus change during a tab animation.
         if (mAnimationCount > 0) {
             return;
@@ -1858,7 +2080,14 @@ public class BrowserActivity extends Activity
             delay = TAB_ANIMATION_DURATION + TAB_OVERVIEW_DELAY;
             tabPicker(false, mTabControl.getTabIndex(t), false);
         }
-        sendAnimateFromOverview(t, false, url, delay, null);
+        sendAnimateFromOverview(t, false, urlData, null, delay, null);
+    }
+
+    // A wrapper function of {@link #openTabAndShow(UrlData, Message, boolean, String)}
+    // that accepts url as string.
+    private TabControl.Tab openTabAndShow(String url, final Message msg,
+            boolean closeOnExit, String appId) {
+        return openTabAndShow(new UrlData(url), msg, closeOnExit, appId);
     }
 
     // This method does a ton of stuff. It will attempt to create a new tab
@@ -1869,7 +2098,7 @@ public class BrowserActivity extends Activity
     // the given Message. If the tab overview is already showing (i.e. this
     // method is called from TabListener.onClick(), the method will animate
     // away from the tab overview.
-    private void openTabAndShow(String url, final Message msg,
+    private TabControl.Tab openTabAndShow(UrlData urlData, final Message msg,
             boolean closeOnExit, String appId) {
         final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
         final TabControl.Tab currentTab = mTabControl.getCurrentTab();
@@ -1878,14 +2107,14 @@ public class BrowserActivity extends Activity
             // If the tab overview is up and there are animations, just load
             // the url.
             if (mTabOverview != null && mAnimationCount > 0) {
-                if (url != null) {
+                if (!urlData.isEmpty()) {
                     // We should not have a msg here since onCreateWindow
                     // checks the animation count and every other caller passes
                     // null.
                     assert msg == null;
                     // just dismiss the subwindow and load the given url.
                     dismissSubWindow(currentTab);
-                    currentTab.getWebView().loadUrl(url);
+                    urlData.loadIn(currentTab.getWebView());
                 }
             } else {
                 // show mTabOverview if it is not there.
@@ -1898,23 +2127,25 @@ public class BrowserActivity extends Activity
                 }
                 // Animate from the Tab overview after any animations have
                 // finished.
-                sendAnimateFromOverview(
-                        mTabControl.createNewTab(closeOnExit, appId, url), true,
-                        url, delay, msg);
+                final TabControl.Tab tab = mTabControl.createNewTab(
+                        closeOnExit, appId, urlData.mUrl);
+                sendAnimateFromOverview(tab, true, urlData, null, delay, msg);
+                return tab;
             }
-        } else if (url != null) {
+        } else if (!urlData.isEmpty()) {
             // We should not have a msg here.
             assert msg == null;
             if (mTabOverview != null && mAnimationCount == 0) {
-                sendAnimateFromOverview(currentTab, false, url,
+                sendAnimateFromOverview(currentTab, false, urlData, null,
                         TAB_OVERVIEW_DELAY, null);
             } else {
                 // Get rid of the subwindow if it exists
                 dismissSubWindow(currentTab);
                 // Load the given url.
-                currentTab.getWebView().loadUrl(url);
+                urlData.loadIn(currentTab.getWebView());
             }
         }
+        return currentTab;
     }
 
     private Animation createTabAnimation(final AnimatingView view,
@@ -2126,19 +2357,23 @@ public class BrowserActivity extends Activity
                     .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;
     }
 
-    private void openTab(String url) {
+    private TabControl.Tab openTab(String url) {
         if (mSettings.openInBackground()) {
             TabControl.Tab t = mTabControl.createNewTab();
             if (t != null) {
                 t.getWebView().loadUrl(url);
             }
+            return t;
         } else {
-            openTabAndShow(url, null, false, null);
+            return openTabAndShow(url, null, false, null);
         }
     }
 
@@ -2238,7 +2473,11 @@ public class BrowserActivity extends Activity
         // While the tab overview is animating or being shown, block changes
         // to the title.
         if (mAnimationCount == 0 && mTabOverview == null) {
-            setTitle(buildUrlTitle(url, title));
+            if (CUSTOM_BROWSER_BAR) {
+                mTitleBar.setTitleAndUrl(title, url);
+            } else {
+                setTitle(buildUrlTitle(url, title));
+            }
         }
     }
 
@@ -2279,7 +2518,7 @@ public class BrowserActivity extends Activity
      * or an empty string if, for example, the URL in question is a
      * file:// URL with no hostname.
      */
-    private static String buildTitleUrl(String url) {
+    /* package */ static String buildTitleUrl(String url) {
         String titleUrl = null;
 
         if (url != null) {
@@ -2315,18 +2554,34 @@ public class BrowserActivity extends Activity
         if (mAnimationCount > 0 || mTabOverview != null) {
             return;
         }
-        Drawable[] array = new Drawable[2];
-        PaintDrawable p = new PaintDrawable(Color.WHITE);
-        p.setCornerRadius(3f);
-        array[0] = p;
-        if (icon == null) {
-            array[1] = mGenericFavicon;
+        if (CUSTOM_BROWSER_BAR) {
+            Drawable[] array = new Drawable[3];
+            array[0] = new PaintDrawable(Color.BLACK);
+            PaintDrawable p = new PaintDrawable(Color.WHITE);
+            array[1] = p;
+            if (icon == null) {
+                array[2] = mGenericFavicon;
+            } else {
+                array[2] = new BitmapDrawable(icon);
+            }
+            LayerDrawable d = new LayerDrawable(array);
+            d.setLayerInset(1, 1, 1, 1, 1);
+            d.setLayerInset(2, 2, 2, 2, 2);
+            mTitleBar.setFavicon(d);
         } else {
-            array[1] = new BitmapDrawable(icon);
+            Drawable[] array = new Drawable[2];
+            PaintDrawable p = new PaintDrawable(Color.WHITE);
+            p.setCornerRadius(3f);
+            array[0] = p;
+            if (icon == null) {
+                array[1] = mGenericFavicon;
+            } else {
+                array[1] = new BitmapDrawable(icon);
+            }
+            LayerDrawable d = new LayerDrawable(array);
+            d.setLayerInset(1, 2, 2, 2, 2);
+            getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d);
         }
-        LayerDrawable d = new LayerDrawable(array);
-        d.setLayerInset(1, 2, 2, 2, 2);
-        getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d);
     }
 
     /**
@@ -2346,7 +2601,7 @@ public class BrowserActivity extends Activity
     private void revertLockIcon() {
         mLockIconType = mPrevLockType;
 
-        if (Config.LOGV) {
+        if (LOGV_ENABLED) {
             Log.v(LOGTAG, "BrowserActivity.revertLockIcon:" +
                   " revert lock icon to " + mLockIconType);
         }
@@ -2362,7 +2617,8 @@ public class BrowserActivity extends Activity
         // Change to the parent tab
         final TabControl.Tab tab = mTabControl.getTab(indexToShow);
         if (tab != null) {
-            sendAnimateFromOverview(tab, false, null, delay, null);
+            sendAnimateFromOverview(tab, false, EMPTY_URL_DATA, null, delay,
+                    null);
         } else {
             // Increment this here so that no other animations can happen in
             // between the end of the tab picker transition and the beginning
@@ -2404,9 +2660,9 @@ public class BrowserActivity extends Activity
                         finish();
                         return;
                     }
-                    // call pauseWebView() now, we won't be able to call it in
-                    // onPause() as the WebView won't be valid.
-                    pauseWebView();
+                    // call pauseWebViewTimers() now, we won't be able to call
+                    // it in onPause() as the WebView won't be valid.
+                    pauseWebViewTimers();
                     removeTabFromContentView(current);
                     mTabControl.removeTab(current);
                 }
@@ -2572,7 +2828,12 @@ public class BrowserActivity extends Activity
                             loadURL(getTopWindow(), url);
                             break;
                         case R.id.open_newtab_context_menu_id:
-                            openTab(url);
+                            final TabControl.Tab parent = mTabControl
+                                    .getCurrentTab();
+                            final TabControl.Tab newTab = openTab(url);
+                            if (newTab != parent) {
+                                parent.addChildTab(newTab);
+                            }
                             break;
                         case R.id.bookmark_context_menu_id:
                             Intent intent = new Intent(BrowserActivity.this,
@@ -2697,8 +2958,9 @@ public class BrowserActivity extends Activity
 
             if (!mPageStarted) {
                 mPageStarted = true;
-                // if onResume() has been called, resumeWebView() does nothing.
-                resumeWebView();
+                // if onResume() has been called, resumeWebViewTimers() does
+                // nothing.
+                resumeWebViewTimers();
             }
 
             // reset sync timer to avoid sync starts during loading a page
@@ -2741,7 +3003,7 @@ public class BrowserActivity extends Activity
                     String uiInfo = "UI thread used "
                             + (SystemClock.currentThreadTimeMillis() - mUiStart)
                             + " ms";
-                    if (Config.LOGD) {
+                    if (LOGD_ENABLED) {
                         Log.d(LOGTAG, uiInfo);
                     }
                     //The string that gets written to the log
@@ -2758,7 +3020,7 @@ public class BrowserActivity extends Activity
                             + " ms and irq took "
                             + (sysCpu[4] + sysCpu[5] + sysCpu[6] - mIrqStart)
                             * 10 + " ms, " + uiInfo;
-                    if (Config.LOGD) {
+                    if (LOGD_ENABLED) {
                         Log.d(LOGTAG, performanceString + "\nWebpage: " + url);
                     }
                     if (url != null) {
@@ -2773,7 +3035,7 @@ public class BrowserActivity extends Activity
                         } else if (newUrl.startsWith("https://")) {
                             newUrl = newUrl.substring(8);
                         }
-                        if (Config.LOGD) {
+                        if (LOGD_ENABLED) {
                             Log.d(LOGTAG, newUrl + " loaded");
                         }
                         /*
@@ -2819,9 +3081,9 @@ public class BrowserActivity extends Activity
 
             if (mPageStarted) {
                 mPageStarted = false;
-                // pauseWebView() will do nothing and return false if onPause()
-                // is not called yet.
-                if (pauseWebView()) {
+                // pauseWebViewTimers() will do nothing and return false if
+                // onPause() is not called yet.
+                if (pauseWebViewTimers()) {
                     if (mWakeLock.isHeld()) {
                         mHandler.removeMessages(RELEASE_WAKELOCK);
                         mWakeLock.release();
@@ -2905,7 +3167,7 @@ public class BrowserActivity extends Activity
                     // If NOT a 'safe' url, change the lock to mixed content!
                     if (!(URLUtil.isHttpsUrl(url) || URLUtil.isDataUrl(url) || URLUtil.isAboutUrl(url))) {
                         mLockIconType = LOCK_ICON_MIXED;
-                        if (Config.LOGV) {
+                        if (LOGV_ENABLED) {
                             Log.v(LOGTAG, "BrowserActivity.updateLockIcon:" +
                                   " updated lock icon to " + mLockIconType + " due to " + url);
                         }
@@ -3232,8 +3494,11 @@ public class BrowserActivity extends Activity
                 // openTabAndShow will dispatch the message after creating the
                 // new WebView. This will prevent another request from coming
                 // in during the animation.
-                openTabAndShow(null, msg, false, null);
-                parent.addChildTab(mTabControl.getCurrentTab());
+                final TabControl.Tab newTab =
+                        openTabAndShow(EMPTY_URL_DATA, msg, false, null);
+                if (newTab != parent) {
+                    parent.addChildTab(newTab);
+                }
                 WebView.WebViewTransport transport =
                         (WebView.WebViewTransport) msg.obj;
                 transport.setWebView(mTabControl.getCurrentWebView());
@@ -3339,8 +3604,13 @@ public class BrowserActivity extends Activity
             // Block progress updates to the title bar while the tab overview
             // is animating or being displayed.
             if (mAnimationCount == 0 && mTabOverview == null) {
-                getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
-                        newProgress * 100);
+                if (CUSTOM_BROWSER_BAR) {
+                    mTitleBar.setProgress(newProgress);
+                } else {
+                    getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+                            newProgress * 100);
+
+                }
             }
 
             if (newProgress == 100) {
@@ -3365,7 +3635,7 @@ public class BrowserActivity extends Activity
 
         @Override
         public void onReceivedTitle(WebView view, String title) {
-            String url = view.getOriginalUrl();
+            String url = view.getUrl();
 
             // here, if url is null, we want to reset the title
             setUrlTitle(url, title);
@@ -3374,6 +3644,8 @@ public class BrowserActivity extends Activity
                 url.length() >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) {
                 return;
             }
+            // See if we can find the current url in our history database and
+            // add the new title to it.
             if (url.startsWith("http://www.")) {
                 url = url.substring(11);
             } else if (url.startsWith("http://")) {
@@ -3388,15 +3660,12 @@ public class BrowserActivity extends Activity
                 Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
                     Browser.HISTORY_PROJECTION, where, selArgs, null);
                 if (c.moveToFirst()) {
-                    if (Config.LOGV) {
-                        Log.v(LOGTAG, "updating cursor");
-                    }
                     // Current implementation of database only has one entry per
                     // url.
-                    int titleIndex =
-                            c.getColumnIndex(Browser.BookmarkColumns.TITLE);
-                    c.updateString(titleIndex, title);
-                    c.commitUpdates();
+                    ContentValues map = new ContentValues();
+                    map.put(Browser.BookmarkColumns.TITLE, title);
+                    mResolver.update(Browser.BOOKMARKS_URI, map,
+                            "_id = " + c.getInt(0), null);
                 }
                 c.close();
             } catch (IllegalStateException e) {
@@ -3410,6 +3679,50 @@ public class BrowserActivity extends Activity
         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 +
+                      ")");
+            }
+            mWebStorageQuotaUpdater = quotaUpdater;
+            String DIALOG_PACKAGE = "com.android.browser";
+            String DIALOG_CLASS = DIALOG_PACKAGE + ".PermissionDialog";
+            Intent intent = new Intent();
+            intent.setClassName(DIALOG_PACKAGE, DIALOG_CLASS);
+            intent.putExtra(PermissionDialog.PARAM_ORIGIN, url);
+            intent.putExtra(PermissionDialog.PARAM_QUOTA, currentQuota);
+            startActivityForResult(intent, WEBSTORAGE_QUOTA_DIALOG);
+        }
+
+        /* Adds a JavaScript error message to the system log.
+         * @param message The error message to report.
+         * @param lineNumber The line number of the error.
+         * @param sourceID The name of the source file that caused the error.
+         */
+        @Override
+        public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+            Log.w(LOGTAG, "Console: " + message + " (" + sourceID + ":" + lineNumber + ")");
+        }
+
     };
 
     /**
@@ -3438,7 +3751,7 @@ public class BrowserActivity extends Activity
                     startActivity(intent);
                     return;
                 } catch (ActivityNotFoundException ex) {
-                    if (Config.LOGD) {
+                    if (LOGD_ENABLED) {
                         Log.d(LOGTAG, "activity not found for " + mimetype
                                 + " over " + Uri.parse(url).getScheme(), ex);
                     }
@@ -3525,19 +3838,19 @@ public class BrowserActivity extends Activity
         String cookies = CookieManager.getInstance().getCookie(url);
 
         ContentValues values = new ContentValues();
-        values.put(Downloads.URI, uri.toString());
-        values.put(Downloads.COOKIE_DATA, cookies);
-        values.put(Downloads.USER_AGENT, userAgent);
-        values.put(Downloads.NOTIFICATION_PACKAGE,
+        values.put(Downloads.COLUMN_URI, uri.toString());
+        values.put(Downloads.COLUMN_COOKIE_DATA, cookies);
+        values.put(Downloads.COLUMN_USER_AGENT, userAgent);
+        values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE,
                 getPackageName());
-        values.put(Downloads.NOTIFICATION_CLASS,
+        values.put(Downloads.COLUMN_NOTIFICATION_CLASS,
                 BrowserDownloadPage.class.getCanonicalName());
-        values.put(Downloads.VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
-        values.put(Downloads.MIMETYPE, mimetype);
-        values.put(Downloads.FILENAME_HINT, filename);
-        values.put(Downloads.DESCRIPTION, uri.getHost());
+        values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+        values.put(Downloads.COLUMN_MIME_TYPE, mimetype);
+        values.put(Downloads.COLUMN_FILE_NAME_HINT, filename);
+        values.put(Downloads.COLUMN_DESCRIPTION, uri.getHost());
         if (contentLength > 0) {
-            values.put(Downloads.TOTAL_BYTES, contentLength);
+            values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength);
         }
         if (mimetype == null) {
             // We must have long pressed on a link or image to download it. We
@@ -3562,7 +3875,7 @@ public class BrowserActivity extends Activity
         mLockIconType = LOCK_ICON_UNSECURE;
         if (URLUtil.isHttpsUrl(url)) {
             mLockIconType = LOCK_ICON_SECURE;
-            if (Config.LOGV) {
+            if (LOGV_ENABLED) {
                 Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
                       " reset lock icon to " + mLockIconType);
             }
@@ -3582,7 +3895,7 @@ public class BrowserActivity extends Activity
 
         mLockIconType = LOCK_ICON_UNSECURE;
 
-        if (Config.LOGV) {
+        if (LOGV_ENABLED) {
           Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
                 " reset lock icon to " + mLockIconType);
         }
@@ -3603,7 +3916,11 @@ public class BrowserActivity extends Activity
         // If the tab overview is animating or being shown, do not update the
         // lock icon.
         if (mAnimationCount == 0 && mTabOverview == null) {
-            getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d);
+            if (CUSTOM_BROWSER_BAR) {
+                mTitleBar.setLock(d);
+            } else {
+                getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d);
+            }
         }
     }
 
@@ -4104,8 +4421,8 @@ public class BrowserActivity extends Activity
                         // middle of an animation, animate away from it to the
                         // current tab.
                         if (mTabOverview != null && mAnimationCount == 0) {
-                            sendAnimateFromOverview(currentTab, false, data,
-                                    TAB_OVERVIEW_DELAY, null);
+                            sendAnimateFromOverview(currentTab, false, new UrlData(data),
+                                    null, TAB_OVERVIEW_DELAY, null);
                         } else {
                             dismissSubWindow(currentTab);
                             if (data != null && data.length() != 0) {
@@ -4115,6 +4432,14 @@ public class BrowserActivity extends Activity
                     }
                 }
                 break;
+            case WEBSTORAGE_QUOTA_DIALOG:
+                long currentQuota = 0;
+                if (resultCode == RESULT_OK && intent != null) {
+                    currentQuota = intent.getLongExtra(
+                        PermissionDialog.PARAM_QUOTA, currentQuota);
+                }
+                mWebStorageQuotaUpdater.updateQuota(currentQuota);
+                break;
             default:
                 break;
         }
@@ -4140,7 +4465,7 @@ public class BrowserActivity extends Activity
     private class TabListener implements ImageGrid.Listener {
         public void remove(int position) {
             // Note: Remove is not enabled if we have only one tab.
-            if (Config.DEBUG && mTabControl.getTabCount() == 1) {
+            if (DEBUG && mTabControl.getTabCount() == 1) {
                 throw new AssertionError();
             }
 
@@ -4156,7 +4481,8 @@ public class BrowserActivity extends Activity
                 if (mTabControl.getTabCount() == 0) {
                     current = mTabControl.createNewTab();
                     sendAnimateFromOverview(current, true,
-                            mSettings.getHomePage(), TAB_OVERVIEW_DELAY, null);
+                            new UrlData(mSettings.getHomePage()), null, TAB_OVERVIEW_DELAY,
+                            null);
                 } else {
                     final int index = position > 0 ? (position - 1) : 0;
                     current = mTabControl.getTab(index);
@@ -4188,16 +4514,12 @@ public class BrowserActivity extends Activity
                 }
             }
 
-            // 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);
             } else {
                 sendAnimateFromOverview(mTabControl.getTab(index),
-                        false, null, 0, null);
+                        false, EMPTY_URL_DATA, null, 0, null);
             }
         }
     }
@@ -4219,13 +4541,19 @@ public class BrowserActivity extends Activity
         AnimatingView(Context ctxt, TabControl.Tab t) {
             super(ctxt);
             mTab = t;
-            // Use the top window in the animation since the tab overview will
-            // display the top window in each cell.
-            final WebView w = t.getTopWindow();
-            mPicture = w.capturePicture();
-            mScale = w.getScale() / w.getWidth();
-            mScrollX = w.getScrollX();
-            mScrollY = w.getScrollY();
+            if (t != null && t.getTopWindow() != null) {
+                // Use the top window in the animation since the tab overview
+                // will display the top window in each cell.
+                final WebView w = t.getTopWindow();
+                mPicture = w.capturePicture();
+                mScale = w.getScale() / w.getWidth();
+                mScrollX = w.getScrollX();
+                mScrollY = w.getScrollY();
+            } else {
+                mPicture = null;
+                mScale = 1.0f;
+                mScrollX = mScrollY = 0;
+            }
         }
 
         @Override
@@ -4299,16 +4627,20 @@ public class BrowserActivity extends Activity
         mAnimationCount++;
         // Always change the title bar to the window overview title while
         // animating.
-        getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, null);
-        getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, null);
-        getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
-                Window.PROGRESS_VISIBILITY_OFF);
-        setTitle(R.string.tab_picker_title);
+        if (CUSTOM_BROWSER_BAR) {
+            mTitleBar.setToTabPicker();
+        } else {
+            getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, null);
+            getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, null);
+            getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
+                    Window.PROGRESS_VISIBILITY_OFF);
+            setTitle(R.string.tab_picker_title);
+        }
         // Make the menu empty until the animation completes.
         mMenuState = EMPTY_MENU;
     }
 
-    private void bookmarksOrHistoryPicker(boolean startWithHistory) {
+    /* package */ void bookmarksOrHistoryPicker(boolean startWithHistory) {
         WebView current = mTabControl.getCurrentWebView();
         if (current == null) {
             return;
@@ -4384,11 +4716,11 @@ public class BrowserActivity extends Activity
       return 0;
     }
 
-    static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
+    protected static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
             "(?i)" + // switch on case insensitive matching
             "(" +    // begin group for schema
             "(?:http|https|file):\\/\\/" +
-            "|(?:data|about|content|javascript):" +
+            "|(?:inline|data|about|content|javascript):" +
             ")" +
             "(.*)" );
 
@@ -4409,46 +4741,34 @@ public class BrowserActivity extends Activity
 
         Matcher matcher = ACCEPTED_URI_SCHEMA.matcher(inUrl);
         if (matcher.matches()) {
-            if (hasSpace) {
-                inUrl = inUrl.replace(" ", "%20");
-            }
             // force scheme to lowercase
             String scheme = matcher.group(1);
             String lcScheme = scheme.toLowerCase();
             if (!lcScheme.equals(scheme)) {
-                return lcScheme + matcher.group(2);
+                inUrl = lcScheme + matcher.group(2);
+            }
+            if (hasSpace) {
+                inUrl = inUrl.replace(" ", "%20");
             }
             return inUrl;
         }
         if (hasSpace) {
-            // FIXME: quick search, need to be customized by setting
-            if (inUrl.length() > 2 && inUrl.charAt(1) == ' ') {
-                // FIXME: Is this the correct place to add to searches?
-                // what if someone else calls this function?
-                char char0 = inUrl.charAt(0);
-
-                if (char0 == 'g') {
-                    Browser.addSearchUrl(mResolver, inUrl);
-                    return composeSearchUrl(inUrl.substring(2));
-
-                } else if (char0 == 'w') {
-                    Browser.addSearchUrl(mResolver, inUrl);
-                    return URLUtil.composeSearchUrl(inUrl.substring(2),
-                            QuickSearch_W,
-                            QUERY_PLACE_HOLDER);
-
-                } else if (char0 == 'd') {
-                    Browser.addSearchUrl(mResolver, inUrl);
-                    return URLUtil.composeSearchUrl(inUrl.substring(2),
-                            QuickSearch_D,
-                            QUERY_PLACE_HOLDER);
-
-                } else if (char0 == 'l') {
-                    Browser.addSearchUrl(mResolver, inUrl);
+            // FIXME: Is this the correct place to add to searches?
+            // what if someone else calls this function?
+            int shortcut = parseUrlShortcut(inUrl);
+            if (shortcut != SHORTCUT_INVALID) {
+                Browser.addSearchUrl(mResolver, inUrl);
+                String query = inUrl.substring(2);
+                switch (shortcut) {
+                case SHORTCUT_GOOGLE_SEARCH:
+                    return composeSearchUrl(query);
+                case SHORTCUT_WIKIPEDIA_SEARCH:
+                    return URLUtil.composeSearchUrl(query, QuickSearch_W, QUERY_PLACE_HOLDER);
+                case SHORTCUT_DICTIONARY_SEARCH:
+                    return URLUtil.composeSearchUrl(query, QuickSearch_D, QUERY_PLACE_HOLDER);
+                case SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH:
                     // FIXME: we need location in this case
-                    return URLUtil.composeSearchUrl(inUrl.substring(2),
-                            QuickSearch_L,
-                            QUERY_PLACE_HOLDER);
+                    return URLUtil.composeSearchUrl(query, QuickSearch_L, QUERY_PLACE_HOLDER);
                 }
             }
         } else {
@@ -4482,11 +4802,30 @@ public class BrowserActivity extends Activity
              * mcc-specific xml files.)
              */
             Locale l = Locale.getDefault();
+            String language = l.getLanguage();
+            String country = l.getCountry().toLowerCase();
+            // Chinese and Portuguese have two langauge variants.
+            if ("zh".equals(language)) {
+                if ("cn".equals(country)) {
+                    language = "zh-CN";
+                } else if ("tw".equals(country)) {
+                    language = "zh-TW";
+                }
+            } else if ("pt".equals(language)) {
+                if ("br".equals(country)) {
+                    language = "pt-BR";
+                } else if ("pt".equals(country)) {
+                    language = "pt-PT";
+                }
+            }
             QuickSearch_G = getResources().getString(
-                    R.string.google_search_base, l.getLanguage(),
-                    l.getCountry().toLowerCase())
+                    R.string.google_search_base,
+                    language,
+                    country)
                     + "client=ms-"
                     + Partner.getString(this.getContentResolver(), Partner.CLIENT_ID)
+                    // FIXME, remove this when GEOLOCATION team make their move
+                    + "&action=devloc"
                     + "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&q=%s";
         } else {
             QuickSearch_G = url;
@@ -4634,6 +4973,8 @@ public class BrowserActivity extends Activity
 
     private Toast mStopToast;
 
+    private TitleBar mTitleBar;
+
     // Used during animations to prevent other animations from being triggered.
     // A count is used since the animation to and from the Window overview can
     // overlap. A count of 0 means no animation where a count of > 0 means
@@ -4651,11 +4992,60 @@ public class BrowserActivity extends Activity
     private IntentFilter mNetworkStateChangedFilter;
     private BroadcastReceiver mNetworkStateIntentReceiver;
 
+    private BroadcastReceiver mPackageInstallationReceiver;
+
     // activity requestCode
-    final static int COMBO_PAGE             = 1;
-    final static int DOWNLOAD_PAGE          = 2;
-    final static int PREFERENCES_PAGE       = 3;
+    final static int COMBO_PAGE                 = 1;
+    final static int DOWNLOAD_PAGE              = 2;
+    final static int PREFERENCES_PAGE           = 3;
+    final static int WEBSTORAGE_QUOTA_DIALOG    = 4;
 
     // the frenquency of checking whether system memory is low
     final static int CHECK_MEMORY_INTERVAL = 30000;     // 30 seconds
+
+    /**
+     * A UrlData class to abstract how the content will be set to WebView.
+     * This base class uses loadUrl to show the content.
+     */
+    private static class UrlData {
+        String mUrl;
+        
+        UrlData(String url) {
+            this.mUrl = url;
+        }
+        
+        boolean isEmpty() {
+            return mUrl == null || mUrl.length() == 0;
+        }
+
+        private void loadIn(WebView webView) {
+            webView.loadUrl(mUrl);
+        }
+    };
+
+    /**
+     * A subclass of UrlData class that can display inlined content using
+     * {@link WebView#loadDataWithBaseURL(String, String, String, String, String)}.
+     */
+    private static class InlinedUrlData extends UrlData {
+        InlinedUrlData(String inlined, String mimeType, String encoding, String failUrl) {
+            super(failUrl);
+            mInlined = inlined;
+            mMimeType = mimeType;
+            mEncoding = encoding;
+        }
+        String mMimeType;
+        String mInlined;
+        String mEncoding;
+        
+        boolean isEmpty() {
+            return mInlined == null || mInlined.length() == 0 || super.isEmpty(); 
+        }
+
+        void loadIn(WebView webView) {
+            webView.loadDataWithBaseURL(null, mInlined, mMimeType, mEncoding, mUrl);
+        }
+    }
+
+    private static final UrlData EMPTY_URL_DATA = new UrlData(null);
 }