OSDN Git Service

Treat voice searches from within the browser differently
[android-x86/packages-apps-Browser.git] / src / com / android / browser / BrowserActivity.java
index 9030cf7..1f969af 100644 (file)
@@ -16,9 +16,6 @@
 
 package com.android.browser;
 
-import com.google.android.googleapps.IGoogleLoginService;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
-
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.ProgressDialog;
@@ -26,6 +23,8 @@ import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -33,34 +32,24 @@ import android.content.Context;
 import android.content.DialogInterface;
 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;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
+import android.database.DatabaseUtils;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
-import android.graphics.DrawFilter;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.Picture;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.hardware.SensorListener;
-import android.hardware.SensorManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.net.WebAddress;
-import android.net.http.EventHandler;
 import android.net.http.SslCertificate;
 import android.net.http.SslError;
 import android.os.AsyncTask;
@@ -68,11 +57,9 @@ import android.os.Bundle;
 import android.os.Debug;
 import android.os.Environment;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.provider.Browser;
@@ -80,12 +67,13 @@ import android.provider.ContactsContract;
 import android.provider.ContactsContract.Intents.Insert;
 import android.provider.Downloads;
 import android.provider.MediaStore;
+import android.speech.RecognizerResultsIntent;
 import android.text.IClipboard;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
-import android.text.util.Regex;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Patterns;
 import android.view.ContextMenu;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -99,39 +87,35 @@ import android.view.Window;
 import android.view.WindowManager;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.MenuItem.OnMenuItemClickListener;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.TranslateAnimation;
 import android.webkit.CookieManager;
 import android.webkit.CookieSyncManager;
 import android.webkit.DownloadListener;
-import android.webkit.GeolocationPermissions;
 import android.webkit.HttpAuthHandler;
 import android.webkit.PluginManager;
 import android.webkit.SslErrorHandler;
 import android.webkit.URLUtil;
 import android.webkit.ValueCallback;
 import android.webkit.WebChromeClient;
-import android.webkit.WebChromeClient.CustomViewCallback;
 import android.webkit.WebHistoryItem;
 import android.webkit.WebIconDatabase;
-import android.webkit.WebStorage;
 import android.webkit.WebView;
-import android.webkit.WebViewClient;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.Toast;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.accounts.AccountManagerCallback;
+
+import com.android.common.Search;
+import com.android.common.speech.LoggingEvents;
 
-import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
@@ -141,18 +125,17 @@ import java.net.URL;
 import java.net.URLEncoder;
 import java.text.ParseException;
 import java.util.Date;
-import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Vector;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 
 public class BrowserActivity extends Activity
-    implements View.OnCreateContextMenuListener,
-        DownloadListener {
+    implements 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".
@@ -161,11 +144,6 @@ public class BrowserActivity extends Activity
     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;
-
     // These are single-character shortcuts for searching popular sources.
     private static final int SHORTCUT_INVALID = 0;
     private static final int SHORTCUT_GOOGLE_SEARCH = 1;
@@ -173,108 +151,6 @@ public class BrowserActivity extends Activity
     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;
-
-    static {
-        sWhiteList = new HashSet<String>();
-        sWhiteList.add("cnn.com/");
-        sWhiteList.add("espn.go.com/");
-        sWhiteList.add("nytimes.com/");
-        sWhiteList.add("engadget.com/");
-        sWhiteList.add("yahoo.com/");
-        sWhiteList.add("msn.com/");
-        sWhiteList.add("amazon.com/");
-        sWhiteList.add("consumerist.com/");
-        sWhiteList.add("google.com/m/news");
-    }
-    */
-
-    private void setupHomePage() {
-        final Runnable getAccount = new Runnable() {
-            public void run() {
-                // Lower priority
-                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                // get the default home page
-                String homepage = mSettings.getHomePage();
-
-                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);
-
-                    // three cases:
-                    //
-                    //   hostedUser == googleUser
-                    //      The device has only a google account
-                    //
-                    //   hostedUser != googleUser
-                    //      The device has a hosted account and a google account
-                    //
-                    //   hostedUser != null, googleUser == null
-                    //      The device has only a hosted account (so far)
-
-                    // developers might have no accounts at all
-                    if (hostedUser == null) return;
-
-                    if (googleUser == null || !hostedUser.equals(googleUser)) {
-                        String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
-                        homepage = homepage.replace("?", "/a/" + domain + "?");
-                    }
-                } catch (RemoteException ignore) {
-                    // Login service died; carry on
-                } catch (RuntimeException ignore) {
-                    // Login service died; carry on
-                } finally {
-                    finish(homepage);
-                }
-            }
-
-            private void finish(final String homepage) {
-                mHandler.post(new Runnable() {
-                    public void run() {
-                        mSettings.setHomePage(BrowserActivity.this, homepage);
-                        resumeAfterCredentials();
-
-                        // as this is running in a separate thread,
-                        // BrowserActivity's onDestroy() may have been called,
-                        // which also calls unbindService().
-                        if (mGlsConnection != null) {
-                            // we no longer need to keep GLS open
-                            unbindService(mGlsConnection);
-                            mGlsConnection = null;
-                        }
-                    } });
-            } };
-
-        final boolean[] done = { false };
-
-        // Open a connection to the Google Login Service.  The first
-        // time the connection is established, set up the homepage depending on
-        // the account in a background thread.
-        mGlsConnection = new ServiceConnection() {
-            public void onServiceConnected(ComponentName className, IBinder service) {
-                mGls = IGoogleLoginService.Stub.asInterface(service);
-                if (done[0] == false) {
-                    done[0] = true;
-                    Thread account = new Thread(getAccount);
-                    account.setName("GLSAccount");
-                    account.start();
-                }
-            }
-            public void onServiceDisconnected(ComponentName className) {
-                mGls = null;
-            }
-        };
-
-        bindService(GoogleLoginServiceConstants.SERVICE_INTENT,
-                    mGlsConnection, Context.BIND_AUTO_CREATE);
-    }
-
     private static class ClearThumbnails extends AsyncTask<File, Void, Void> {
         @Override
         public Void doInBackground(File... files) {
@@ -295,7 +171,8 @@ public class BrowserActivity extends Activity
      */
     private FrameLayout mBrowserFrameLayout;
 
-    @Override public void onCreate(Bundle icicle) {
+    @Override
+    public void onCreate(Bundle icicle) {
         if (LOGV_ENABLED) {
             Log.v(LOGTAG, this + " onStart");
         }
@@ -303,6 +180,12 @@ public class BrowserActivity extends Activity
         // test the browser in OpenGL
         // requestWindowFeature(Window.FEATURE_OPENGL);
 
+        // enable this to test the browser in 32bit
+        if (false) {
+            getWindow().setFormat(PixelFormat.RGBX_8888);
+            BitmapFactory.setDefaultConfig(Bitmap.Config.ARGB_8888);
+        }
+
         setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
 
         mResolver = getContentResolver();
@@ -314,16 +197,6 @@ public class BrowserActivity extends Activity
             return;
         }
 
-        //
-        // start MASF proxy service
-        //
-        //Intent proxyServiceIntent = new Intent();
-        //proxyServiceIntent.setComponent
-        //    (new ComponentName(
-        //        "com.android.masfproxyservice",
-        //        "com.android.masfproxyservice.MasfProxyService"));
-        //startService(proxyServiceIntent, null);
-
         mSecLockIcon = Resources.getSystem().getDrawable(
                 android.R.drawable.ic_secure);
         mMixLockIcon = Resources.getSystem().getDrawable(
@@ -341,6 +214,9 @@ public class BrowserActivity extends Activity
                 .findViewById(R.id.fullscreen_custom_content);
         frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);
         mTitleBar = new TitleBar(this);
+        // mTitleBar will be always shown in the fully loaded mode
+        mTitleBar.setProgress(100);
+        mFakeTitleBar = new TitleBar(this);
 
         // Create the tab control and our initial tab
         mTabControl = new TabControl(this);
@@ -351,11 +227,18 @@ public class BrowserActivity extends Activity
         // Keep a settings instance handy.
         mSettings = BrowserSettings.getInstance();
         mSettings.setTabControl(mTabControl);
-        mSettings.loadFromDb(this);
 
         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");
 
+        // Find out if the network is currently up.
+        ConnectivityManager cm = (ConnectivityManager) getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        NetworkInfo info = cm.getActiveNetworkInfo();
+        if (info != null) {
+            mIsNetworkUp = info.isAvailable();
+        }
+
         /* enables registration for changes in network status from
            http stack */
         mNetworkStateChangedFilter = new IntentFilter();
@@ -366,9 +249,15 @@ public class BrowserActivity extends Activity
                 public void onReceive(Context context, Intent intent) {
                     if (intent.getAction().equals(
                             ConnectivityManager.CONNECTIVITY_ACTION)) {
-                        boolean noConnectivity = intent.getBooleanExtra(
-                                ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
-                        onNetworkToggle(!noConnectivity);
+
+                        NetworkInfo info = intent.getParcelableExtra(
+                                ConnectivityManager.EXTRA_NETWORK_INFO);
+                        String typeName = info.getTypeName();
+                        String subtypeName = info.getSubtypeName();
+                        sendNetworkType(typeName.toLowerCase(),
+                                (subtypeName != null ? subtypeName.toLowerCase() : ""));
+
+                        onNetworkToggle(info.isAvailable());
                     }
                 }
             };
@@ -388,6 +277,12 @@ public class BrowserActivity extends Activity
                     // if it is replacing, refreshPlugins() when adding
                     return;
                 }
+
+                if (sGoogleApps.contains(packageName)) {
+                    BrowserActivity.this.packageChanged(packageName,
+                            Intent.ACTION_PACKAGE_ADDED.equals(action));
+                }
+
                 PackageManager pm = BrowserActivity.this.getPackageManager();
                 PackageInfo pkgInfo = null;
                 try {
@@ -435,9 +330,12 @@ public class BrowserActivity extends Activity
             // the tab will be close when exit.
             UrlData urlData = getUrlDataFromIntent(intent);
 
-            final TabControl.Tab t = mTabControl.createNewTab(
-                    Intent.ACTION_VIEW.equals(intent.getAction()) &&
-                    intent.getData() != null,
+            String action = intent.getAction();
+            final Tab t = mTabControl.createNewTab(
+                    (Intent.ACTION_VIEW.equals(action) &&
+                    intent.getData() != null)
+                    || RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS
+                    .equals(action),
                     intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), urlData.mUrl);
             mTabControl.setCurrentTab(t);
             attachTabToContentView(t);
@@ -448,27 +346,11 @@ public class BrowserActivity extends Activity
                     webView.setInitialScale(scale);
                 }
             }
-            // If we are not restoring from an icicle, then there is a high
-            // likely hood this is the first run. So, check to see if the
-            // homepage needs to be configured and copy any plugins from our
-            // asset directory to the data partition.
-            if ((extra == null || !extra.getBoolean("testing"))
-                    && !mSettings.isLoginInitialized()) {
-                setupHomePage();
-            }
 
             if (urlData.isEmpty()) {
-                if (mSettings.isLoginInitialized()) {
-                    webView.loadUrl(mSettings.getHomePage());
-                } else {
-                    waitForCredentials();
-                }
+                loadUrl(webView, mSettings.getHomePage());
             } else {
-                if (extra != null) {
-                    urlData.setPostData(extra
-                            .getByteArray(Browser.EXTRA_POST_DATA));
-                }
-                urlData.loadIn(webView);
+                loadUrlDataIn(t, urlData);
             }
         } else {
             // TabControl.restoreState() will create a new tab even if
@@ -481,11 +363,37 @@ public class BrowserActivity extends Activity
         if (jsFlags.trim().length() != 0) {
             mTabControl.getCurrentWebView().setJsFlags(jsFlags);
         }
+        // Work out which packages are installed on the system.
+        getInstalledPackages();
+
+        // Start watching the default geolocation permissions
+        mSystemAllowGeolocationOrigins
+                = new SystemAllowGeolocationOrigins(getApplicationContext());
+        mSystemAllowGeolocationOrigins.start();
+    }
+
+    /**
+     * Feed the previously stored results strings to the BrowserProvider so that
+     * the SearchDialog will show them instead of the standard searches.
+     * @param result String to show on the editable line of the SearchDialog.
+     */
+    /* package */ void showVoiceSearchResults(String result) {
+        ContentProviderClient client = mResolver.acquireContentProviderClient(
+                Browser.BOOKMARKS_URI);
+        ContentProvider prov = client.getLocalContentProvider();
+        BrowserProvider bp = (BrowserProvider) prov;
+        bp.setQueryResults(mTabControl.getCurrentTab().getVoiceSearchResults());
+        client.release();
+
+        Bundle bundle = createGoogleSearchSourceBundle(
+                GOOGLE_SEARCH_SOURCE_SEARCHKEY);
+        bundle.putBoolean(SearchManager.CONTEXT_IS_VOICE, true);
+        startSearch(result, false, bundle, false);
     }
 
     @Override
     protected void onNewIntent(Intent intent) {
-        TabControl.Tab current = mTabControl.getCurrentTab();
+        Tab current = mTabControl.getCurrentTab();
         // When a tab is closed on exit, the current tab index is set to -1.
         // Reset before proceed as Browser requires the current tab to be set.
         if (current == null) {
@@ -506,10 +414,39 @@ public class BrowserActivity extends Activity
             // just resume the browser
             return;
         }
+        // In case the SearchDialog is open.
+        ((SearchManager) getSystemService(Context.SEARCH_SERVICE))
+                .stopSearch();
+        boolean activateVoiceSearch = RecognizerResultsIntent
+                .ACTION_VOICE_SEARCH_RESULTS.equals(action);
         if (Intent.ACTION_VIEW.equals(action)
                 || Intent.ACTION_SEARCH.equals(action)
                 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
-                || Intent.ACTION_WEB_SEARCH.equals(action)) {
+                || Intent.ACTION_WEB_SEARCH.equals(action)
+                || activateVoiceSearch) {
+            if (current.isInVoiceSearchMode()) {
+                String title = current.getVoiceDisplayTitle();
+                if (title != null && title.equals(intent.getStringExtra(
+                        SearchManager.QUERY))) {
+                    // The user submitted the same search as the last voice
+                    // search, so do nothing.
+                    return;
+                }
+                if (Intent.ACTION_SEARCH.equals(action)
+                        && current.voiceSearchSourceIsGoogle()) {
+                    Intent logIntent = new Intent(
+                            LoggingEvents.ACTION_LOG_EVENT);
+                    logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
+                            LoggingEvents.VoiceSearch.QUERY_UPDATED);
+                    logIntent.putExtra(
+                            LoggingEvents.VoiceSearch.EXTRA_QUERY_UPDATED_VALUE,
+                            intent.getDataString());
+                    sendBroadcast(logIntent);
+                    // Note, onPageStarted will revert the voice title bar
+                    // When http://b/issue?id=2379215 is fixed, we should update
+                    // the title bar here.
+                }
+            }
             // 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)) {
@@ -520,15 +457,16 @@ public class BrowserActivity extends Activity
             if (urlData.isEmpty()) {
                 urlData = new UrlData(mSettings.getHomePage());
             }
-            urlData.setPostData(intent
-                    .getByteArrayExtra(Browser.EXTRA_POST_DATA));
 
             final String appId = intent
                     .getStringExtra(Browser.EXTRA_APPLICATION_ID);
-            if (Intent.ACTION_VIEW.equals(action)
+            if ((Intent.ACTION_VIEW.equals(action)
+                    // If a voice search has no appId, it means that it came
+                    // from the browser.  In that case, reuse the current tab.
+                    || (activateVoiceSearch && appId != null))
                     && !getPackageName().equals(appId)
                     && (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
-                TabControl.Tab appTab = mTabControl.getTabFromId(appId);
+                Tab appTab = mTabControl.getTabFromId(appId);
                 if (appTab != null) {
                     Log.i(LOGTAG, "Reusing tab for " + appId);
                     // Dismiss the subwindow if applicable.
@@ -540,19 +478,19 @@ 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, urlData.mUrl);
+                            mTabControl.recreateWebView(appTab, urlData);
 
                     if (current != appTab) {
                         switchToTab(mTabControl.getTabIndex(appTab));
                         if (needsLoad) {
-                            urlData.loadIn(appTab.getWebView());
+                            loadUrlDataIn(appTab, urlData);
                         }
                     } else {
                         // If the tab was the current tab, we have to attach
                         // it to the view system again.
                         attachTabToContentView(appTab);
                         if (needsLoad) {
-                            urlData.loadIn(appTab.getWebView());
+                            loadUrlDataIn(appTab, urlData);
                         }
                     }
                     return;
@@ -575,13 +513,33 @@ public class BrowserActivity extends Activity
                     }
                 }
             } else {
-                if ("about:debug".equals(urlData.mUrl)) {
-                    mSettings.toggleDebugSettings();
+                if (!urlData.isEmpty()
+                        && urlData.mUrl.startsWith("about:debug")) {
+                    if ("about:debug.dom".equals(urlData.mUrl)) {
+                        current.getWebView().dumpDomTree(false);
+                    } else if ("about:debug.dom.file".equals(urlData.mUrl)) {
+                        current.getWebView().dumpDomTree(true);
+                    } else if ("about:debug.render".equals(urlData.mUrl)) {
+                        current.getWebView().dumpRenderTree(false);
+                    } else if ("about:debug.render.file".equals(urlData.mUrl)) {
+                        current.getWebView().dumpRenderTree(true);
+                    } else if ("about:debug.display".equals(urlData.mUrl)) {
+                        current.getWebView().dumpDisplayTree();
+                    } else if (urlData.mUrl.startsWith("about:debug.drag")) {
+                        int index = urlData.mUrl.codePointAt(16) - '0';
+                        if (index <= 0 || index > 9) {
+                            current.getWebView().setDragTracker(null);
+                        } else {
+                            current.getWebView().setDragTracker(new MeshTracker(index));
+                        }
+                    } else {
+                        mSettings.toggleDebugSettings();
+                    }
                     return;
                 }
                 // Get rid of the subwindow if it exists
                 dismissSubWindow(current);
-                urlData.loadIn(current.getWebView());
+                loadUrlDataIn(current, urlData);
             }
         }
     }
@@ -611,6 +569,10 @@ public class BrowserActivity extends Activity
 
         String url = null;
         final String action = intent.getAction();
+        if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS.equals(
+                action)) {
+            return false;
+        }
         if (Intent.ACTION_VIEW.equals(action)) {
             Uri data = intent.getData();
             if (data != null) url = data.toString();
@@ -637,14 +599,21 @@ public class BrowserActivity extends Activity
 
         // 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()
+        if (Patterns.WEB_URL.matcher(url).matches()
                 || ACCEPTED_URI_SCHEMA.matcher(url).matches()
                 || parseUrlShortcut(url) != SHORTCUT_INVALID) {
             return false;
         }
 
-        Browser.updateVisitedHistory(mResolver, url, false);
-        Browser.addSearchUrl(mResolver, url);
+        final ContentResolver cr = mResolver;
+        final String newUrl = url;
+        new AsyncTask<Void, Void, Void>() {
+            protected Void doInBackground(Void... unused) {
+                Browser.updateVisitedHistory(cr, newUrl, false);
+                Browser.addSearchUrl(cr, newUrl);
+                return null;
+            }
+        }.execute();
 
         Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
@@ -662,7 +631,8 @@ public class BrowserActivity extends Activity
     }
 
     private UrlData getUrlDataFromIntent(Intent intent) {
-        String url = null;
+        String url = "";
+        Map<String, String> headers = null;
         if (intent != null) {
             final String action = intent.getAction();
             if (Intent.ACTION_VIEW.equals(action)) {
@@ -674,12 +644,17 @@ 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));
+                if (url != null && url.startsWith("http")) {
+                    final Bundle pairs = intent
+                            .getBundleExtra(Browser.EXTRA_HEADERS);
+                    if (pairs != null && !pairs.isEmpty()) {
+                        Iterator<String> iter = pairs.keySet().iterator();
+                        headers = new HashMap<String, String>();
+                        while (iter.hasNext()) {
+                            String key = iter.next();
+                            headers.put(key, pairs.getString(key));
+                        }
+                    }
                 }
             } else if (Intent.ACTION_SEARCH.equals(action)
                     || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
@@ -687,21 +662,24 @@ public class BrowserActivity extends Activity
                 url = intent.getStringExtra(SearchManager.QUERY);
                 if (url != null) {
                     mLastEnteredUrl = url;
-                    // Don't add Urls, just search terms.
-                    // Urls will get added when the page is loaded.
-                    if (!Regex.WEB_URL_PATTERN.matcher(url).matches()) {
-                        Browser.updateVisitedHistory(mResolver, url, false);
-                    }
                     // In general, we shouldn't modify URL from Intent.
                     // But currently, we get the user-typed URL from search box as well.
                     url = fixUrl(url);
                     url = smartUrlFilter(url);
+                    final ContentResolver cr = mResolver;
+                    final String newUrl = url;
+                    new AsyncTask<Void, Void, Void>() {
+                        protected Void doInBackground(Void... unused) {
+                            Browser.updateVisitedHistory(cr, newUrl, false);
+                            return null;
+                        }
+                    }.execute();
                     String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&";
                     if (url.contains(searchSource)) {
                         String source = null;
                         final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
                         if (appData != null) {
-                            source = appData.getString(SearchManager.SOURCE);
+                            source = appData.getString(Search.SOURCE);
                         }
                         if (TextUtils.isEmpty(source)) {
                             source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
@@ -711,9 +689,22 @@ public class BrowserActivity extends Activity
                 }
             }
         }
-        return new UrlData(url);
+        return new UrlData(url, headers, intent);
     }
+    /* package */ void showVoiceTitleBar(String title) {
+        mTitleBar.setInVoiceMode(true);
+        mFakeTitleBar.setInVoiceMode(true);
 
+        mTitleBar.setDisplayTitle(title);
+        mFakeTitleBar.setDisplayTitle(title);
+    }
+    /* package */ void revertVoiceTitleBar() {
+        mTitleBar.setInVoiceMode(false);
+        mFakeTitleBar.setInVoiceMode(false);
+
+        mTitleBar.setDisplayTitle(mUrl);
+        mFakeTitleBar.setDisplayTitle(mUrl);
+    }
     /* package */ static String fixUrl(String inUrl) {
         // FIXME: Converting the url to lower case
         // duplicates functionality in smartUrlFilter().
@@ -744,100 +735,8 @@ public class BrowserActivity extends Activity
         return inUrl;
     }
 
-    /**
-     * Looking for the pattern like this
-     *
-     *          *
-     *         * *
-     *      ***   *     *******
-     *             *   *
-     *              * *
-     *               *
-     */
-    private final SensorListener mSensorListener = new SensorListener() {
-        private long mLastGestureTime;
-        private float[] mPrev = new float[3];
-        private float[] mPrevDiff = new float[3];
-        private float[] mDiff = new float[3];
-        private float[] mRevertDiff = new float[3];
-
-        public void onSensorChanged(int sensor, float[] values) {
-            boolean show = false;
-            float[] diff = new float[3];
-
-            for (int i = 0; i < 3; i++) {
-                diff[i] = values[i] - mPrev[i];
-                if (Math.abs(diff[i]) > 1) {
-                    show = true;
-                }
-                if ((diff[i] > 1.0 && mDiff[i] < 0.2)
-                        || (diff[i] < -1.0 && mDiff[i] > -0.2)) {
-                    // start track when there is a big move, or revert
-                    mRevertDiff[i] = mDiff[i];
-                    mDiff[i] = 0;
-                } else if (diff[i] > -0.2 && diff[i] < 0.2) {
-                    // reset when it is flat
-                    mDiff[i] = mRevertDiff[i]  = 0;
-                }
-                mDiff[i] += diff[i];
-                mPrevDiff[i] = diff[i];
-                mPrev[i] = values[i];
-            }
-
-            if (false) {
-                // only shows if we think the delta is big enough, in an attempt
-                // to detect "serious" moves left/right or up/down
-                Log.d("BrowserSensorHack", "sensorChanged " + sensor + " ("
-                        + values[0] + ", " + values[1] + ", " + values[2] + ")"
-                        + " diff(" + diff[0] + " " + diff[1] + " " + diff[2]
-                        + ")");
-                Log.d("BrowserSensorHack", "      mDiff(" + mDiff[0] + " "
-                        + mDiff[1] + " " + mDiff[2] + ")" + " mRevertDiff("
-                        + mRevertDiff[0] + " " + mRevertDiff[1] + " "
-                        + mRevertDiff[2] + ")");
-            }
-
-            long now = android.os.SystemClock.uptimeMillis();
-            if (now - mLastGestureTime > 1000) {
-                mLastGestureTime = 0;
-
-                float y = mDiff[1];
-                float z = mDiff[2];
-                float ay = Math.abs(y);
-                float az = Math.abs(z);
-                float ry = mRevertDiff[1];
-                float rz = mRevertDiff[2];
-                float ary = Math.abs(ry);
-                float arz = Math.abs(rz);
-                boolean gestY = ay > 2.5f && ary > 1.0f && ay > ary;
-                boolean gestZ = az > 3.5f && arz > 1.0f && az > arz;
-
-                if ((gestY || gestZ) && !(gestY && gestZ)) {
-                    WebView view = mTabControl.getCurrentWebView();
-
-                    if (view != null) {
-                        if (gestZ) {
-                            if (z < 0) {
-                                view.zoomOut();
-                            } else {
-                                view.zoomIn();
-                            }
-                        } else {
-                            view.flingScroll(0, Math.round(y * 100));
-                        }
-                    }
-                    mLastGestureTime = now;
-                }
-            }
-        }
-
-        public void onAccuracyChanged(int sensor, int accuracy) {
-            // TODO Auto-generated method stub
-
-        }
-    };
-
-    @Override protected void onResume() {
+    @Override
+    protected void onResume() {
         super.onResume();
         if (LOGV_ENABLED) {
             Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this);
@@ -857,51 +756,19 @@ public class BrowserActivity extends Activity
             mWakeLock.release();
         }
 
-        if (mCredsDlg != null) {
-            if (!mHandler.hasMessages(CANCEL_CREDS_REQUEST)) {
-             // In case credential request never comes back
-                mHandler.sendEmptyMessageDelayed(CANCEL_CREDS_REQUEST, 6000);
-            }
-        }
-
         registerReceiver(mNetworkStateIntentReceiver,
                          mNetworkStateChangedFilter);
         WebView.enablePlatformNotifications();
-
-        if (mSettings.doFlick()) {
-            if (mSensorManager == null) {
-                mSensorManager = (SensorManager) getSystemService(
-                        Context.SENSOR_SERVICE);
-            }
-            mSensorManager.registerListener(mSensorListener,
-                    SensorManager.SENSOR_ACCELEROMETER,
-                    SensorManager.SENSOR_DELAY_FASTEST);
-        } else {
-            mSensorManager = null;
-        }
     }
 
     /**
      * Since the actual title bar is embedded in the WebView, and removing it
-     * would change its appearance, create a temporary title bar to go at
-     * the top of the screen while the menu is open.
+     * would change its appearance, use a different TitleBar to show overlayed
+     * at the top of the screen, when the menu is open or the page is loading.
      */
     private TitleBar mFakeTitleBar;
 
     /**
-     * Holder for the fake title bar.  It will have a foreground shadow, as well
-     * as a white background, so the fake title bar looks like the real one.
-     */
-    private ViewGroup mFakeTitleBarHolder;
-
-    /**
-     * Layout parameters for the fake title bar within mFakeTitleBarHolder
-     */
-    private FrameLayout.LayoutParams mFakeTitleBarParams
-            = new FrameLayout.LayoutParams(
-            ViewGroup.LayoutParams.FILL_PARENT,
-            ViewGroup.LayoutParams.WRAP_CONTENT);
-    /**
      * Keeps track of whether the options menu is open.  This is important in
      * determining whether to show or hide the title bar overlay.
      */
@@ -955,54 +822,14 @@ public class BrowserActivity extends Activity
         return true;
     }
 
-    /**
-     * Special class used exclusively for the shadow drawn underneath the fake
-     * title bar.  The shadow does not need to be drawn if the WebView
-     * underneath is scrolled to the top, because it will draw directly on top
-     * of the embedded shadow.
-     */
-    private static class Shadow extends View {
-        private WebView mWebView;
-
-        public Shadow(Context context, AttributeSet attrs) {
-            super(context, attrs);
-        }
-
-        public void setWebView(WebView view) {
-            mWebView = view;
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            // In general onDraw is the method to override, but we care about
-            // whether or not the background gets drawn, which happens in draw()
-            if (mWebView == null || mWebView.getScrollY() > getHeight()) {
-                super.draw(canvas);
-            }
-            // Need to invalidate so that if the scroll position changes, we
-            // still draw as appropriate.
-            invalidate();
-        }
-    }
-
     private void showFakeTitleBar() {
-        final View decor = getWindow().peekDecorView();
-        if (mFakeTitleBar == null && mActiveTabsPage == null
-                && !mActivityInPause && decor != null
-                && decor.getWindowToken() != null) {
-            Rect visRect = new Rect();
-            if (!mBrowserFrameLayout.getGlobalVisibleRect(visRect)) {
-                if (LOGD_ENABLED) {
-                    Log.d(LOGTAG, "showFakeTitleBar visRect failed");
-                }
+        if (mFakeTitleBar.getParent() == null && mActiveTabsPage == null
+                && !mActivityInPause) {
+            WebView mainView = mTabControl.getCurrentWebView();
+            // if there is no current WebView, don't show the faked title bar;
+            if (mainView == null) {
                 return;
             }
-            final WebView webView = getTopWindow();
-            mFakeTitleBar = new TitleBar(this);
-            mFakeTitleBar.setTitleAndUrl(null, webView.getUrl());
-            mFakeTitleBar.setProgress(webView.getProgress());
-            mFakeTitleBar.setFavicon(webView.getFavicon());
-            updateLockIconToLatest();
 
             WindowManager manager
                     = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
@@ -1011,30 +838,15 @@ public class BrowserActivity extends Activity
             // while the menu is up
             WindowManager.LayoutParams params
                     = new WindowManager.LayoutParams(
-                    ViewGroup.LayoutParams.FILL_PARENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT,
                     ViewGroup.LayoutParams.WRAP_CONTENT,
-                    WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
+                    WindowManager.LayoutParams.TYPE_APPLICATION,
                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                     PixelFormat.TRANSLUCENT);
             params.gravity = Gravity.TOP;
-            WebView mainView = mTabControl.getCurrentWebView();
-            boolean atTop = mainView != null && mainView.getScrollY() == 0;
+            boolean atTop = mainView.getScrollY() == 0;
             params.windowAnimations = atTop ? 0 : R.style.TitleBar;
-            // XXX : Without providing an offset, the fake title bar will be
-            // placed underneath the status bar.  Use the global visible rect
-            // of mBrowserFrameLayout to determine the bottom of the status bar
-            params.y = visRect.top;
-            // Add a holder for the title bar.  It also holds a shadow to show
-            // below the title bar.
-            if (mFakeTitleBarHolder == null) {
-                mFakeTitleBarHolder = (ViewGroup) LayoutInflater.from(this)
-                    .inflate(R.layout.title_bar_bg, null);
-            }
-            Shadow shadow = (Shadow) mFakeTitleBarHolder.findViewById(
-                    R.id.shadow);
-            shadow.setWebView(mainView);
-            mFakeTitleBarHolder.addView(mFakeTitleBar, 0, mFakeTitleBarParams);
-            manager.addView(mFakeTitleBarHolder, params);
+            manager.addView(mFakeTitleBar, params);
         }
     }
 
@@ -1050,10 +862,11 @@ public class BrowserActivity extends Activity
             showFakeTitleBar();
         }
     }
+
     private void hideFakeTitleBar() {
-        if (mFakeTitleBar == null) return;
+        if (mFakeTitleBar.getParent() == null) return;
         WindowManager.LayoutParams params = (WindowManager.LayoutParams)
-                mFakeTitleBarHolder.getLayoutParams();
+                mFakeTitleBar.getLayoutParams();
         WebView mainView = mTabControl.getCurrentWebView();
         // Although we decided whether or not to animate based on the current
         // scroll position, the scroll position may have changed since the
@@ -1063,10 +876,8 @@ public class BrowserActivity extends Activity
                 ? 0 : R.style.TitleBar;
         WindowManager manager
                     = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
-        manager.updateViewLayout(mFakeTitleBarHolder, params);
-        mFakeTitleBarHolder.removeView(mFakeTitleBar);
-        manager.removeView(mFakeTitleBarHolder);
-        mFakeTitleBar = null;
+        manager.updateViewLayout(mFakeTitleBar, params);
+        manager.removeView(mFakeTitleBar);
     }
 
     /**
@@ -1081,12 +892,21 @@ public class BrowserActivity extends Activity
         openContextMenu(mTitleBar);
     }
 
+    @Override
+    public void onContextMenuClosed(Menu menu) {
+        super.onContextMenuClosed(menu);
+        if (mInLoad) {
+            showFakeTitleBar();
+        }
+    }
+
     /**
      *  onSaveInstanceState(Bundle map)
      *  onSaveInstanceState is called right before onStop(). The map contains
      *  the saved state.
      */
-    @Override protected void onSaveInstanceState(Bundle outState) {
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
         if (LOGV_ENABLED) {
             Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this);
         }
@@ -1100,7 +920,8 @@ public class BrowserActivity extends Activity
         mTabControl.saveState(outState);
     }
 
-    @Override protected void onPause() {
+    @Override
+    protected void onPause() {
         super.onPause();
 
         if (mActivityInPause) {
@@ -1116,12 +937,6 @@ public class BrowserActivity extends Activity
                     .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
         }
 
-        // Clear the credentials toast if it is up
-        if (mCredsDlg != null && mCredsDlg.isShowing()) {
-            mCredsDlg.dismiss();
-        }
-        mCredsDlg = null;
-
         // FIXME: This removes the active tabs page and resets the menu to
         // MAIN_MENU.  A better solution might be to do this work in onNewIntent
         // but then we would need to save it in onSaveInstanceState and restore
@@ -1135,22 +950,27 @@ public class BrowserActivity extends Activity
         // unregister network state listener
         unregisterReceiver(mNetworkStateIntentReceiver);
         WebView.disablePlatformNotifications();
-
-        if (mSensorManager != null) {
-            mSensorManager.unregisterListener(mSensorListener);
-        }
     }
 
-    @Override protected void onDestroy() {
+    @Override
+    protected void onDestroy() {
         if (LOGV_ENABLED) {
             Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this);
         }
         super.onDestroy();
 
+        if (mUploadMessage != null) {
+            mUploadMessage.onReceiveValue(null);
+            mUploadMessage = null;
+        }
+
         if (mTabControl == null) return;
 
+        // Remove the fake title bar if it is there
+        hideFakeTitleBar();
+
         // Remove the current tab and sub window
-        TabControl.Tab t = mTabControl.getCurrentTab();
+        Tab t = mTabControl.getCurrentTab();
         if (t != null) {
             dismissSubWindow(t);
             removeTabFromContentView(t);
@@ -1158,22 +978,12 @@ public class BrowserActivity extends Activity
         // Destroy all the tabs
         mTabControl.destroy();
         WebIconDatabase.getInstance().close();
-        if (mGlsConnection != null) {
-            unbindService(mGlsConnection);
-            mGlsConnection = null;
-        }
-
-        //
-        // stop MASF proxy service
-        //
-        //Intent proxyServiceIntent = new Intent();
-        //proxyServiceIntent.setComponent
-        //   (new ComponentName(
-        //        "com.android.masfproxyservice",
-        //        "com.android.masfproxyservice.MasfProxyService"));
-        //stopService(proxyServiceIntent);
 
         unregisterReceiver(mPackageInstallationReceiver);
+
+        // Stop watching the default geolocation permissions
+        mSystemAllowGeolocationOrigins.stop();
+        mSystemAllowGeolocationOrigins = null;
     }
 
     @Override
@@ -1185,7 +995,7 @@ public class BrowserActivity extends Activity
             mPageInfoDialog.dismiss();
             showPageInfo(
                 mPageInfoView,
-                mPageInfoFromShowSSLCertificateOnError.booleanValue());
+                mPageInfoFromShowSSLCertificateOnError);
         }
         if (mSSLCertificateDialog != null) {
             mSSLCertificateDialog.dismiss();
@@ -1213,32 +1023,31 @@ public class BrowserActivity extends Activity
             showHttpAuthentication(mHttpAuthHandler, null, null, title,
                     name, password, focusId);
         }
-        if (mFindDialog != null && mFindDialog.isShowing()) {
-            mFindDialog.onConfigurationChanged(newConfig);
-        }
     }
 
-    @Override public void onLowMemory() {
+    @Override
+    public void onLowMemory() {
         super.onLowMemory();
         mTabControl.freeMemory();
     }
 
-    private boolean resumeWebViewTimers() {
-        if ((!mActivityInPause && !mPageStarted) ||
-                (mActivityInPause && mPageStarted)) {
+    private void resumeWebViewTimers() {
+        Tab tab = mTabControl.getCurrentTab();
+        if (tab == null) return; // monkey can trigger this
+        boolean inLoad = tab.inLoad();
+        if ((!mActivityInPause && !inLoad) || (mActivityInPause && inLoad)) {
             CookieSyncManager.getInstance().startSync();
-            WebView w = mTabControl.getCurrentWebView();
+            WebView w = tab.getWebView();
             if (w != null) {
                 w.resumeTimers();
             }
-            return true;
-        } else {
-            return false;
         }
     }
 
     private boolean pauseWebViewTimers() {
-        if (mActivityInPause && !mPageStarted) {
+        Tab tab = mTabControl.getCurrentTab();
+        boolean inLoad = tab.inLoad();
+        if (mActivityInPause && !inLoad) {
             CookieSyncManager.getInstance().stopSync();
             WebView w = mTabControl.getCurrentWebView();
             if (w != null) {
@@ -1250,75 +1059,24 @@ public class BrowserActivity extends Activity
         }
     }
 
-    // FIXME: Do we want to call this when loading google for the first time?
-    /*
-     * This function is called when we are launching for the first time. We
-     * are waiting for the login credentials before loading Google home
-     * pages. This way the user will be logged in straight away.
-     */
-    private void waitForCredentials() {
-        // Show a toast
-        mCredsDlg = new ProgressDialog(this);
-        mCredsDlg.setIndeterminate(true);
-        mCredsDlg.setMessage(getText(R.string.retrieving_creds_dlg_msg));
-        // If the user cancels the operation, then cancel the Google
-        // Credentials request.
-        mCredsDlg.setCancelMessage(mHandler.obtainMessage(CANCEL_CREDS_REQUEST));
-        mCredsDlg.show();
-
-        // We set a timeout for the retrieval of credentials in onResume()
-        // as that is when we have freed up some CPU time to get
-        // the login credentials.
-    }
-
-    /*
-     * If we have received the credentials or we have timed out and we are
-     * showing the credentials dialog, then it is time to move on.
-     */
-    private void resumeAfterCredentials() {
-        if (mCredsDlg == null) {
-            return;
-        }
-
-        // Clear the toast
-        if (mCredsDlg.isShowing()) {
-            mCredsDlg.dismiss();
-        }
-        mCredsDlg = null;
-
-        // Clear any pending timeout
-        mHandler.removeMessages(CANCEL_CREDS_REQUEST);
-
-        // Load the page
-        WebView w = mTabControl.getCurrentWebView();
-        if (w != null) {
-            w.loadUrl(mSettings.getHomePage());
-        }
-
-        // Update the settings, need to do this last as it can take a moment
-        // to persist the settings. In the mean time we could be loading
-        // content.
-        mSettings.setLoginInitialized(this);
-    }
-
     // Open the icon database and retain all the icons for visited sites.
     private void retainIconsOnStartup() {
         final WebIconDatabase db = WebIconDatabase.getInstance();
         db.open(getDir("icons", 0).getPath());
+        Cursor c = null;
         try {
-            Cursor c = Browser.getAllBookmarks(mResolver);
-            if (!c.moveToFirst()) {
-                c.deactivate();
-                return;
+            c = Browser.getAllBookmarks(mResolver);
+            if (c.moveToFirst()) {
+                int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
+                do {
+                    String url = c.getString(urlIndex);
+                    db.retainIconForPageUrl(url);
+                } while (c.moveToNext());
             }
-            int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
-            do {
-                String url = c.getString(urlIndex);
-                db.retainIconForPageUrl(url);
-            } while (c.moveToNext());
-            c.deactivate();
         } catch (IllegalStateException e) {
             Log.e(LOGTAG, "retainIconsOnStartup", e);
+        } finally {
+            if (c!= null) c.close();
         }
     }
 
@@ -1327,6 +1085,10 @@ public class BrowserActivity extends Activity
         return mTabControl.getCurrentTopWebView();
     }
 
+    TabControl getTabControl() {
+        return mTabControl;
+    }
+
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
@@ -1361,19 +1123,21 @@ public class BrowserActivity extends Activity
         // options selector, so set mCanChord to true so we can access them.
         mCanChord = true;
         int id = item.getItemId();
+        boolean result = true;
         switch (id) {
             // For the context menu from the title bar
-            case R.id.title_bar_share_page_url:
             case R.id.title_bar_copy_page_url:
-                WebView mainView = mTabControl.getCurrentWebView();
-                if (null == mainView) {
-                    return false;
+                Tab currentTab = mTabControl.getCurrentTab();
+                if (null == currentTab) {
+                    result = false;
+                    break;
                 }
-                if (id == R.id.title_bar_share_page_url) {
-                    Browser.sendString(this, mainView.getUrl());
-                } else {
-                    copy(mainView.getUrl());
+                WebView mainView = currentTab.getWebView();
+                if (null == mainView) {
+                    result = false;
+                    break;
                 }
+                copy(mainView.getUrl());
                 break;
             // -- Browser context menu
             case R.id.open_context_menu_id:
@@ -1384,7 +1148,8 @@ public class BrowserActivity extends Activity
             case R.id.copy_link_context_menu_id:
                 final WebView webView = getTopWindow();
                 if (null == webView) {
-                    return false;
+                    result = false;
+                    break;
                 }
                 final HashMap hrefMap = new HashMap();
                 hrefMap.put("webview", webView);
@@ -1395,30 +1160,28 @@ public class BrowserActivity extends Activity
 
             default:
                 // For other context menus
-                return onOptionsItemSelected(item);
+                result = onOptionsItemSelected(item);
         }
         mCanChord = false;
-        return true;
+        return result;
     }
 
     private Bundle createGoogleSearchSourceBundle(String source) {
         Bundle bundle = new Bundle();
-        bundle.putString(SearchManager.SOURCE, source);
+        bundle.putString(Search.SOURCE, source);
         return bundle;
     }
 
-    /**
-     * Overriding this to insert a local information bundle
-     */
-    @Override
-    public boolean onSearchRequested() {
+    /* package */ void editUrl() {
         if (mOptionsMenuOpen) closeOptionsMenu();
         String url = (getTopWindow() == null) ? null : getTopWindow().getUrl();
         startSearch(mSettings.getHomePage().equals(url) ? null : url, true,
-                createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), false);
-        return true;
+                null, false);
     }
 
+    /**
+     * Overriding this to insert a local information bundle
+     */
     @Override
     public void startSearch(String initialQuery, boolean selectInitialQuery,
             Bundle appSearchData, boolean globalSearch) {
@@ -1438,8 +1201,8 @@ public class BrowserActivity extends Activity
      *                 the current one, return false.
      */
     /* package */ boolean switchToTab(int index) {
-        TabControl.Tab tab = mTabControl.getTab(index);
-        TabControl.Tab currentTab = mTabControl.getCurrentTab();
+        Tab tab = mTabControl.getTab(index);
+        Tab currentTab = mTabControl.getCurrentTab();
         if (tab == null || tab == currentTab) {
             return false;
         }
@@ -1455,20 +1218,20 @@ public class BrowserActivity extends Activity
         return true;
     }
 
-    /* package */ TabControl.Tab openTabToHomePage() {
+    /* package */ Tab openTabToHomePage() {
         return openTabAndShow(mSettings.getHomePage(), false, null);
     }
 
     /* package */ void closeCurrentWindow() {
-        final TabControl.Tab current = mTabControl.getCurrentTab();
+        final Tab current = mTabControl.getCurrentTab();
         if (mTabControl.getTabCount() == 1) {
             // This is the last tab.  Open a new one, with the home
             // page and close the current one.
-            TabControl.Tab newTab = openTabToHomePage();
+            openTabToHomePage();
             closeTab(current);
             return;
         }
-        final TabControl.Tab parent = current.getParentTab();
+        final Tab parent = current.getParentTab();
         int indexToShow = -1;
         if (parent != null) {
             indexToShow = mTabControl.getTabIndex(parent);
@@ -1528,7 +1291,7 @@ public class BrowserActivity extends Activity
                 break;
 
             case R.id.goto_menu_id:
-                onSearchRequested();
+                editUrl();
                 break;
 
             case R.id.bookmarks_menu_id:
@@ -1581,16 +1344,18 @@ public class BrowserActivity extends Activity
                 break;
 
             case R.id.homepage_menu_id:
-                TabControl.Tab current = mTabControl.getCurrentTab();
+                Tab current = mTabControl.getCurrentTab();
                 if (current != null) {
                     dismissSubWindow(current);
-                    current.getWebView().loadUrl(mSettings.getHomePage());
+                    loadUrl(current.getWebView(), mSettings.getHomePage());
                 }
                 break;
 
             case R.id.preferences_menu_id:
                 Intent intent = new Intent(this,
                         BrowserPreferencesPage.class);
+                intent.putExtra(BrowserPreferencesPage.CURRENT_PAGE,
+                        getTopWindow().getUrl());
                 startActivityForResult(intent, PREFERENCES_PAGE);
                 break;
 
@@ -1600,6 +1365,7 @@ public class BrowserActivity extends Activity
                 }
                 mFindDialog.setWebView(getTopWindow());
                 mFindDialog.show();
+                getTopWindow().setFindIsUp(true);
                 mMenuState = EMPTY_MENU;
                 break;
 
@@ -1614,15 +1380,27 @@ public class BrowserActivity extends Activity
                 bookmarksOrHistoryPicker(true);
                 break;
 
+            case R.id.title_bar_share_page_url:
             case R.id.share_page_menu_id:
-                Browser.sendString(this, getTopWindow().getUrl(),
-                        getText(R.string.choosertitle_sharevia).toString());
+                Tab currentTab = mTabControl.getCurrentTab();
+                if (null == currentTab) {
+                    mCanChord = false;
+                    return false;
+                }
+                currentTab.populatePickerData();
+                sharePage(this, currentTab.getTitle(),
+                        currentTab.getUrl(), currentTab.getFavicon(),
+                        createScreenshot(currentTab.getWebView()));
                 break;
 
             case R.id.dump_nav_menu_id:
                 getTopWindow().debugDump();
                 break;
 
+            case R.id.dump_counters_menu_id:
+                getTopWindow().dumpV8Counters();
+                break;
+
             case R.id.zoom_in_menu_id:
                 getTopWindow().zoomIn();
                 break;
@@ -1647,7 +1425,7 @@ public class BrowserActivity extends Activity
                     int menuid = item.getItemId();
                     for (int id = 0; id < WINDOW_SHORTCUT_ID_ARRAY.length; id++) {
                         if (WINDOW_SHORTCUT_ID_ARRAY[id] == menuid) {
-                            TabControl.Tab desiredTab = mTabControl.getTab(id);
+                            Tab desiredTab = mTabControl.getTab(id);
                             if (desiredTab != null &&
                                     desiredTab != mTabControl.getCurrentTab()) {
                                 switchToTab(id);
@@ -1672,8 +1450,8 @@ public class BrowserActivity extends Activity
         mMenuState = R.id.MAIN_MENU;
     }
 
-    @Override public boolean onPrepareOptionsMenu(Menu menu)
-    {
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
         // This happens when the user begins to hold down the menu key, so
         // allow them to chord to get a shortcut.
         mCanChord = true;
@@ -1714,7 +1492,7 @@ public class BrowserActivity extends Activity
                         .setEnabled(canGoForward);
 
                 menu.findItem(R.id.new_tab_menu_id).setEnabled(
-                        mTabControl.getTabCount() < TabControl.MAX_TABS);
+                        mTabControl.canCreateNewTab());
 
                 // decide whether to show the share link option
                 PackageManager pm = getPackageManager();
@@ -1727,6 +1505,12 @@ public class BrowserActivity extends Activity
                 final MenuItem nav = menu.findItem(R.id.dump_nav_menu_id);
                 nav.setVisible(isNavDump);
                 nav.setEnabled(isNavDump);
+
+                boolean showDebugSettings = mSettings.showDebugSettings();
+                final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id);
+                counter.setVisible(showDebugSettings);
+                counter.setEnabled(showDebugSettings);
+
                 break;
         }
         mCurrentMenuState = mMenuState;
@@ -1736,6 +1520,9 @@ public class BrowserActivity extends Activity
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v,
             ContextMenuInfo menuInfo) {
+        if (v instanceof TitleBar) {
+            return;
+        }
         WebView webview = (WebView) v;
         WebView.HitTestResult result = webview.getHitTestResult();
         if (result == null) {
@@ -1818,7 +1605,9 @@ public class BrowserActivity extends Activity
                 menu.setHeaderView(titleView);
                 // decide whether to show the open link in new tab option
                 menu.findItem(R.id.open_newtab_context_menu_id).setVisible(
-                        mTabControl.getTabCount() < TabControl.MAX_TABS);
+                        mTabControl.canCreateNewTab());
+                menu.findItem(R.id.bookmark_context_menu_id).setVisible(
+                        Bookmarks.urlHasAcceptableScheme(extra));
                 PackageManager pm = getPackageManager();
                 Intent send = new Intent(Intent.ACTION_SEND);
                 send.setType("text/plain");
@@ -1836,23 +1625,26 @@ public class BrowserActivity extends Activity
                         new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
                 menu.findItem(R.id.download_context_menu_id).
                         setOnMenuItemClickListener(new Download(extra));
+                menu.findItem(R.id.set_wallpaper_context_menu_id).
+                        setOnMenuItemClickListener(new SetAsWallpaper(extra));
                 break;
 
             default:
                 Log.w(LOGTAG, "We should not get here.");
                 break;
         }
+        hideFakeTitleBar();
     }
 
     // Attach the given tab to the content view.
     // this should only be called for the current tab.
-    private void attachTabToContentView(TabControl.Tab t) {
+    private void attachTabToContentView(Tab t) {
         // Attach the container that contains the main WebView and any other UI
         // associated with the tab.
         t.attachTabToContentView(mContentView);
 
         if (mShouldShowErrorConsole) {
-            ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true);
+            ErrorConsoleView errorConsole = t.getErrorConsole(true);
             if (errorConsole.numberOfErrors() == 0) {
                 errorConsole.showConsole(ErrorConsoleView.SHOW_NONE);
             } else {
@@ -1860,89 +1652,67 @@ public class BrowserActivity extends Activity
             }
 
             mErrorConsoleContainer.addView(errorConsole,
-                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
+                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                                   ViewGroup.LayoutParams.WRAP_CONTENT));
         }
 
-        setLockIconType(t.getLockIconType());
-        setPrevLockType(t.getPrevLockIconType());
-
-        // this is to match the code in removeTabFromContentView()
-        if (!mPageStarted && t.getTopWindow().getProgress() < 100) {
-            mPageStarted = true;
-        }
-
         WebView view = t.getWebView();
         view.setEmbeddedTitleBar(mTitleBar);
+        if (t.isInVoiceSearchMode()) {
+            showVoiceTitleBar(t.getVoiceDisplayTitle());
+        } else {
+            revertVoiceTitleBar();
+        }
         // Request focus on the top window.
         t.getTopWindow().requestFocus();
     }
 
     // Attach a sub window to the main WebView of the given tab.
-    private void attachSubWindow(TabControl.Tab t) {
+    void attachSubWindow(Tab t) {
         t.attachSubWindow(mContentView);
         getTopWindow().requestFocus();
     }
 
     // Remove the given tab from the content view.
-    private void removeTabFromContentView(TabControl.Tab t) {
+    private void removeTabFromContentView(Tab t) {
         // Remove the container that contains the main WebView.
         t.removeTabFromContentView(mContentView);
 
-        if (mTabControl.getCurrentErrorConsole(false) != null) {
-            mErrorConsoleContainer.removeView(mTabControl.getCurrentErrorConsole(false));
+        ErrorConsoleView errorConsole = t.getErrorConsole(false);
+        if (errorConsole != null) {
+            mErrorConsoleContainer.removeView(errorConsole);
         }
 
         WebView view = t.getWebView();
         if (view != null) {
             view.setEmbeddedTitleBar(null);
         }
-
-        // unlike attachTabToContentView(), removeTabFromContentView() can be
-        // called for the non-current tab. Need to add the check.
-        if (t == mTabControl.getCurrentTab()) {
-            t.setLockIconType(getLockIconType());
-            t.setPrevLockIconType(getPrevLockType());
-
-            // this is not a perfect solution. But currently there is one
-            // WebViewClient for all the WebView. if user switches from an
-            // in-load window to an already loaded window, mPageStarted will not
-            // be set to false. If user leaves the Browser, pauseWebViewTimers()
-            // won't do anything and leaves the timer running even Browser is in
-            // the background.
-            if (mPageStarted) {
-                mPageStarted = false;
-            }
-        }
     }
 
     // Remove the sub window if it exists. Also called by TabControl when the
     // user clicks the 'X' to dismiss a sub window.
-    /* package */ void dismissSubWindow(TabControl.Tab t) {
+    /* package */ void dismissSubWindow(Tab t) {
         t.removeSubWindow(mContentView);
-        // Tell the TabControl to dismiss the subwindow. This will destroy
-        // the WebView.
-        mTabControl.dismissSubWindow(t);
+        // dismiss the subwindow. This will destroy the WebView.
+        t.dismissSubWindow();
         getTopWindow().requestFocus();
     }
 
     // A wrapper function of {@link #openTabAndShow(UrlData, boolean, String)}
     // that accepts url as string.
-    private TabControl.Tab openTabAndShow(String url, boolean closeOnExit,
-            String appId) {
+    private Tab openTabAndShow(String url, boolean closeOnExit, String appId) {
         return openTabAndShow(new UrlData(url), closeOnExit, appId);
     }
 
     // This method does a ton of stuff. It will attempt to create a new tab
     // if we haven't reached MAX_TABS. Otherwise it uses the current tab. If
     // url isn't null, it will load the given url.
-    /* package */ TabControl.Tab openTabAndShow(UrlData urlData,
-            boolean closeOnExit, String appId) {
-        final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
-        final TabControl.Tab currentTab = mTabControl.getCurrentTab();
-        if (newTab) {
-            final TabControl.Tab tab = mTabControl.createNewTab(
-                    closeOnExit, appId, urlData.mUrl);
+    /* package */Tab openTabAndShow(UrlData urlData, boolean closeOnExit,
+            String appId) {
+        final Tab currentTab = mTabControl.getCurrentTab();
+        if (mTabControl.canCreateNewTab()) {
+            final Tab tab = mTabControl.createNewTab(closeOnExit, appId,
+                    urlData.mUrl);
             WebView webview = tab.getWebView();
             // If the last tab was removed from the active tabs page, currentTab
             // will be null.
@@ -1954,7 +1724,7 @@ public class BrowserActivity extends Activity
             mTabControl.setCurrentTab(tab);
             attachTabToContentView(tab);
             if (!urlData.isEmpty()) {
-                urlData.loadIn(webview);
+                loadUrlDataIn(tab, urlData);
             }
             return tab;
         } else {
@@ -1962,18 +1732,18 @@ public class BrowserActivity extends Activity
             dismissSubWindow(currentTab);
             if (!urlData.isEmpty()) {
                 // Load the given url.
-                urlData.loadIn(currentTab.getWebView());
+                loadUrlDataIn(currentTab, urlData);
             }
+            return currentTab;
         }
-        return currentTab;
     }
 
-    private TabControl.Tab openTab(String url) {
+    private Tab openTab(String url) {
         if (mSettings.openInBackground()) {
-            TabControl.Tab t = mTabControl.createNewTab();
+            Tab t = mTabControl.createNewTab();
             if (t != null) {
                 WebView view = t.getWebView();
-                view.loadUrl(url);
+                loadUrl(view, url);
             }
             return t;
         } else {
@@ -2007,6 +1777,82 @@ public class BrowserActivity extends Activity
         }
     }
 
+    private class SetAsWallpaper extends Thread implements
+            OnMenuItemClickListener, DialogInterface.OnCancelListener {
+        private URL mUrl;
+        private ProgressDialog mWallpaperProgress;
+        private boolean mCanceled = false;
+
+        public SetAsWallpaper(String url) {
+            try {
+                mUrl = new URL(url);
+            } catch (MalformedURLException e) {
+                mUrl = null;
+            }
+        }
+
+        public void onCancel(DialogInterface dialog) {
+            mCanceled = true;
+        }
+
+        public boolean onMenuItemClick(MenuItem item) {
+            if (mUrl != null) {
+                // The user may have tried to set a image with a large file size as their
+                // background so it may take a few moments to perform the operation. Display
+                // a progress spinner while it is working.
+                mWallpaperProgress = new ProgressDialog(BrowserActivity.this);
+                mWallpaperProgress.setIndeterminate(true);
+                mWallpaperProgress.setMessage(getText(R.string.progress_dialog_setting_wallpaper));
+                mWallpaperProgress.setCancelable(true);
+                mWallpaperProgress.setOnCancelListener(this);
+                mWallpaperProgress.show();
+                start();
+            }
+            return true;
+        }
+
+        public void run() {
+            Drawable oldWallpaper = BrowserActivity.this.getWallpaper();
+            try {
+                // TODO: This will cause the resource to be downloaded again, when we
+                // should in most cases be able to grab it from the cache. To fix this
+                // we should query WebCore to see if we can access a cached version and
+                // instead open an input stream on that. This pattern could also be used
+                // in the download manager where the same problem exists.
+                InputStream inputstream = mUrl.openStream();
+                if (inputstream != null) {
+                    setWallpaper(inputstream);
+                }
+            } catch (IOException e) {
+                Log.e(LOGTAG, "Unable to set new wallpaper");
+                // Act as though the user canceled the operation so we try to
+                // restore the old wallpaper.
+                mCanceled = true;
+            }
+
+            if (mCanceled) {
+                // Restore the old wallpaper if the user cancelled whilst we were setting
+                // the new wallpaper.
+                int width = oldWallpaper.getIntrinsicWidth();
+                int height = oldWallpaper.getIntrinsicHeight();
+                Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+                Canvas canvas = new Canvas(bm);
+                oldWallpaper.setBounds(0, 0, width, height);
+                oldWallpaper.draw(canvas);
+                try {
+                    setWallpaper(bm);
+                } catch (IOException e) {
+                    Log.e(LOGTAG, "Unable to restore old wallpaper.");
+                }
+                mCanceled = false;
+            }
+
+            if (mWallpaperProgress.isShowing()) {
+                mWallpaperProgress.dismiss();
+            }
+        }
+    }
+
     private void copy(CharSequence text) {
         try {
             IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
@@ -2027,7 +1873,8 @@ public class BrowserActivity extends Activity
      * call resetTitleAndRevertLockIcon.
      */
     /* package */ void resetTitleAndRevertLockIcon() {
-        revertLockIcon();
+        mTabControl.getCurrentTab().revertLockIcon();
+        updateLockIconToLatest();
         resetTitleIconAndProgress();
     }
 
@@ -2041,7 +1888,7 @@ public class BrowserActivity extends Activity
         }
         resetTitleAndIcon(current);
         int progress = current.getProgress();
-        mWebChromeClient.onProgressChanged(current, progress);
+        current.getWebChromeClient().onProgressChanged(current, progress);
     }
 
     // Reset the title and the icon based on the given item.
@@ -2061,14 +1908,14 @@ public class BrowserActivity extends Activity
      * @param url The URL of the site being loaded.
      * @param title The title of the site being loaded.
      */
-    private void setUrlTitle(String url, String title) {
+    void setUrlTitle(String url, String title) {
         mUrl = url;
         mTitle = title;
 
-        mTitleBar.setTitleAndUrl(title, url);
-        if (mFakeTitleBar != null) {
-            mFakeTitleBar.setTitleAndUrl(title, url);
-        }
+        // If we are in voice search mode, the title has already been set.
+        if (mTabControl.getCurrentTab().isInVoiceSearchMode()) return;
+        mTitleBar.setDisplayTitle(url);
+        mFakeTitleBar.setDisplayTitle(url);
     }
 
     /**
@@ -2109,43 +1956,16 @@ public class BrowserActivity extends Activity
     }
 
     // Set the favicon in the title bar.
-    private void setFavicon(Bitmap icon) {
+    void setFavicon(Bitmap icon) {
         mTitleBar.setFavicon(icon);
-        if (mFakeTitleBar != null) {
-            mFakeTitleBar.setFavicon(icon);
-        }
-    }
-
-    /**
-     * Saves the current lock-icon state before resetting
-     * the lock icon. If we have an error, we may need to
-     * roll back to the previous state.
-     */
-    private void saveLockIcon() {
-        mPrevLockType = mLockIconType;
-    }
-
-    /**
-     * Reverts the lock-icon state to the last saved state,
-     * for example, if we had an error, and need to cancel
-     * the load.
-     */
-    private void revertLockIcon() {
-        mLockIconType = mPrevLockType;
-
-        if (LOGV_ENABLED) {
-            Log.v(LOGTAG, "BrowserActivity.revertLockIcon:" +
-                  " revert lock icon to " + mLockIconType);
-        }
-
-        updateLockIconToLatest();
+        mFakeTitleBar.setFavicon(icon);
     }
 
     /**
      * Close the tab, remove its associated title bar, and adjust mTabControl's
      * current tab to a valid value.
      */
-    /* package */ void closeTab(TabControl.Tab t) {
+    /* package */ void closeTab(Tab t) {
         int currentIndex = mTabControl.getCurrentIndex();
         int removeIndex = mTabControl.getTabIndex(t);
         mTabControl.removeTab(t);
@@ -2156,8 +1976,8 @@ public class BrowserActivity extends Activity
         resetTitleIconAndProgress();
     }
 
-    private void goBackOnePageOrQuit() {
-        TabControl.Tab current = mTabControl.getCurrentTab();
+    /* package */ void goBackOnePageOrQuit() {
+        Tab current = mTabControl.getCurrentTab();
         if (current == null) {
             /*
              * Instead of finishing the activity, simply push this to the back
@@ -2175,17 +1995,17 @@ public class BrowserActivity extends Activity
         } else {
             // Check to see if we are closing a window that was created by
             // another window. If so, we switch back to that window.
-            TabControl.Tab parent = current.getParentTab();
+            Tab parent = current.getParentTab();
             if (parent != null) {
                 switchToTab(mTabControl.getTabIndex(parent));
                 // Now we close the other tab
                 closeTab(current);
             } else {
                 if (current.closeOnExit()) {
-                    // force mPageStarted to be false as we are going to either
-                    // finish the activity or remove the tab. This will ensure
-                    // pauseWebView() taking action.
-                    mPageStarted = false;
+                    // force the tab's inLoad() to be false as we are going to
+                    // either finish the activity or remove the tab. This will
+                    // ensure pauseWebViewTimers() taking action.
+                    mTabControl.getCurrentTab().clearInLoad();
                     if (mTabControl.getTabCount() == 1) {
                         finish();
                         return;
@@ -2218,17 +2038,24 @@ public class BrowserActivity extends Activity
         }
     }
 
+    boolean isMenuDown() {
+        return mMenuIsDown;
+    }
+
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
+        // Even if MENU is already held down, we need to call to super to open
+        // the IME on long press.
+        if (KeyEvent.KEYCODE_MENU == keyCode) {
+            mMenuIsDown = true;
+            return super.onKeyDown(keyCode, event);
+        }
         // The default key mode is DEFAULT_KEYS_SEARCH_LOCAL. As the MENU is
         // still down, we don't want to trigger the search. Pretend to consume
         // the key and do nothing.
         if (mMenuIsDown) return true;
 
         switch(keyCode) {
-            case KeyEvent.KEYCODE_MENU:
-                mMenuIsDown = true;
-                break;
             case KeyEvent.KEYCODE_SPACE:
                 // WebView/WebTextView handle the keys in the KeyDown. As
                 // the Activity's shortcut keys are only handled when WebView
@@ -2263,7 +2090,8 @@ public class BrowserActivity extends Activity
                 if (event.isTracking() && !event.isCanceled()) {
                     if (mCustomView != null) {
                         // if a custom view is showing, hide it
-                        mWebChromeClient.onHideCustomView();
+                        mTabControl.getCurrentWebView().getWebChromeClient()
+                                .onHideCustomView();
                     } else if (mActiveTabsPage != null) {
                         // if tab page is showing, hide it
                         removeActiveTabPage(true);
@@ -2291,7 +2119,12 @@ public class BrowserActivity extends Activity
         resetTitleAndRevertLockIcon();
         WebView w = getTopWindow();
         w.stopLoading();
-        mWebViewClient.onPageFinished(w, w.getUrl());
+        // FIXME: before refactor, it is using mWebViewClient. So I keep the
+        // same logic here. But for subwindow case, should we call into the main
+        // WebView's onPageFinished as we never call its onPageStarted and if
+        // the page finishes itself, we don't call onPageFinished.
+        mTabControl.getCurrentWebView().getWebViewClient().onPageFinished(w,
+                w.getUrl());
 
         cancelStopToast();
         mStopToast = Toast
@@ -2299,6 +2132,10 @@ public class BrowserActivity extends Activity
         mStopToast.show();
     }
 
+    boolean didUserStopLoading() {
+        return mDidStopLoad;
+    }
+
     private void cancelStopToast() {
         if (mStopToast != null) {
             mStopToast.cancel();
@@ -2306,9 +2143,16 @@ public class BrowserActivity extends Activity
         }
     }
 
-    // called by a non-UI thread to post the message
-    public void postMessage(int what, int arg1, int arg2, Object obj) {
-        mHandler.sendMessage(mHandler.obtainMessage(what, arg1, arg2, obj));
+    // called by a UI or non-UI thread to post the message
+    public void postMessage(int what, int arg1, int arg2, Object obj,
+            long delayMillis) {
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(what, arg1, arg2,
+                obj), delayMillis);
+    }
+
+    // called by a UI or non-UI thread to remove the message
+    void removeMessages(int what, Object object) {
+        mHandler.removeMessages(what, object);
     }
 
     // public message ids
@@ -2317,10 +2161,9 @@ public class BrowserActivity extends Activity
 
     // Message Ids
     private static final int FOCUS_NODE_HREF         = 102;
-    private static final int CANCEL_CREDS_REQUEST    = 103;
     private static final int RELEASE_WAKELOCK        = 107;
 
-    private static final int UPDATE_BOOKMARK_THUMBNAIL = 108;
+    static final int UPDATE_BOOKMARK_THUMBNAIL       = 108;
 
     // Private handler for handling javascript and saving passwords
     private Handler mHandler = new Handler() {
@@ -2330,6 +2173,7 @@ public class BrowserActivity extends Activity
                 case FOCUS_NODE_HREF:
                 {
                     String url = (String) msg.getData().get("url");
+                    String title = (String) msg.getData().get("title");
                     if (url == null || url.length() == 0) {
                         break;
                     }
@@ -2342,12 +2186,11 @@ public class BrowserActivity extends Activity
                     switch (msg.arg1) {
                         case R.id.open_context_menu_id:
                         case R.id.view_image_context_menu_id:
-                            loadURL(getTopWindow(), url);
+                            loadUrlFromContext(getTopWindow(), url);
                             break;
                         case R.id.open_newtab_context_menu_id:
-                            final TabControl.Tab parent = mTabControl
-                                    .getCurrentTab();
-                            final TabControl.Tab newTab = openTab(url);
+                            final Tab parent = mTabControl.getCurrentTab();
+                            final Tab newTab = openTab(url);
                             if (newTab != parent) {
                                 parent.addChildTab(newTab);
                             }
@@ -2356,11 +2199,45 @@ public class BrowserActivity extends Activity
                             Intent intent = new Intent(BrowserActivity.this,
                                     AddBookmarkPage.class);
                             intent.putExtra("url", url);
+                            intent.putExtra("title", title);
                             startActivity(intent);
                             break;
                         case R.id.share_link_context_menu_id:
-                            Browser.sendString(BrowserActivity.this, url,
-                                    getText(R.string.choosertitle_sharevia).toString());
+                            // See if this site has been visited before
+                            StringBuilder sb = new StringBuilder(
+                                    Browser.BookmarkColumns.URL + " = ");
+                            DatabaseUtils.appendEscapedSQLString(sb, url);
+                            Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
+                                    Browser.HISTORY_PROJECTION,
+                                    sb.toString(),
+                                    null,
+                                    null);
+                            if (c.moveToFirst()) {
+                                // The site has been visited before, so grab the
+                                // info from the database.
+                                Bitmap favicon = null;
+                                Bitmap thumbnail = null;
+                                String linkTitle = c.getString(Browser.
+                                        HISTORY_PROJECTION_TITLE_INDEX);
+                                byte[] data = c.getBlob(Browser.
+                                        HISTORY_PROJECTION_FAVICON_INDEX);
+                                if (data != null) {
+                                    favicon = BitmapFactory.decodeByteArray(
+                                            data, 0, data.length);
+                                }
+                                data = c.getBlob(Browser.
+                                        HISTORY_PROJECTION_THUMBNAIL_INDEX);
+                                if (data != null) {
+                                    thumbnail = BitmapFactory.decodeByteArray(
+                                            data, 0, data.length);
+                                }
+                                sharePage(BrowserActivity.this,
+                                        linkTitle, url, favicon, thumbnail);
+                            } else {
+                                Browser.sendString(BrowserActivity.this, url,
+                                        getString(
+                                        R.string.choosertitle_sharevia));
+                            }
                             break;
                         case R.id.copy_link_context_menu_id:
                             copy(url);
@@ -2374,20 +2251,20 @@ public class BrowserActivity extends Activity
                 }
 
                 case LOAD_URL:
-                    loadURL(getTopWindow(), (String) msg.obj);
+                    loadUrlFromContext(getTopWindow(), (String) msg.obj);
                     break;
 
                 case STOP_LOAD:
                     stopLoading();
                     break;
 
-                case CANCEL_CREDS_REQUEST:
-                    resumeAfterCredentials();
-                    break;
-
                 case RELEASE_WAKELOCK:
                     if (mWakeLock.isHeld()) {
                         mWakeLock.release();
+                        // if we reach here, Browser should be still in the
+                        // background loading after WAKELOCK_TIMEOUT (5-min).
+                        // To avoid burning the battery, stop loading.
+                        mTabControl.stopAllLoading();
                     }
                     break;
 
@@ -2401,6 +2278,35 @@ public class BrowserActivity extends Activity
         }
     };
 
+    /**
+     * Share a page, providing the title, url, favicon, and a screenshot.  Uses
+     * an {@link Intent} to launch the Activity chooser.
+     * @param c Context used to launch a new Activity.
+     * @param title Title of the page.  Stored in the Intent with
+     *          {@link Intent#EXTRA_SUBJECT}
+     * @param url URL of the page.  Stored in the Intent with
+     *          {@link Intent#EXTRA_TEXT}
+     * @param favicon Bitmap of the favicon for the page.  Stored in the Intent
+     *          with {@link Browser#EXTRA_SHARE_FAVICON}
+     * @param screenshot Bitmap of a screenshot of the page.  Stored in the
+     *          Intent with {@link Browser#EXTRA_SHARE_SCREENSHOT}
+     */
+    public static final void sharePage(Context c, String title, String url,
+            Bitmap favicon, Bitmap screenshot) {
+        Intent send = new Intent(Intent.ACTION_SEND);
+        send.setType("text/plain");
+        send.putExtra(Intent.EXTRA_TEXT, url);
+        send.putExtra(Intent.EXTRA_SUBJECT, title);
+        send.putExtra(Browser.EXTRA_SHARE_FAVICON, favicon);
+        send.putExtra(Browser.EXTRA_SHARE_SCREENSHOT, screenshot);
+        try {
+            c.startActivity(Intent.createChooser(send, c.getString(
+                    R.string.choosertitle_sharevia)));
+        } catch(android.content.ActivityNotFoundException ex) {
+            // if no app handles it, do nothing
+        }
+    }
+
     private void updateScreenshot(WebView view) {
         // If this is a bookmarked site, add a screenshot to the database.
         // FIXME: When should we update?  Every time?
@@ -2477,22 +2383,41 @@ public class BrowserActivity extends Activity
             return null;
         }
         Bitmap bm = Bitmap.createBitmap(getDesiredThumbnailWidth(this),
-                getDesiredThumbnailHeight(this), Bitmap.Config.ARGB_4444);
+                getDesiredThumbnailHeight(this), Bitmap.Config.RGB_565);
         Canvas canvas = new Canvas(bm);
         // May need to tweak these values to determine what is the
         // best scale factor
         int thumbnailWidth = thumbnail.getWidth();
+        int thumbnailHeight = thumbnail.getHeight();
+        float scaleFactorX = 1.0f;
+        float scaleFactorY = 1.0f;
         if (thumbnailWidth > 0) {
-            float scaleFactor = (float) getDesiredThumbnailWidth(this) /
+            scaleFactorX = (float) getDesiredThumbnailWidth(this) /
                     (float)thumbnailWidth;
-            canvas.scale(scaleFactor, scaleFactor);
+        } else {
+            return null;
+        }
+
+        if (view.getWidth() > view.getHeight() &&
+                thumbnailHeight < view.getHeight() && thumbnailHeight > 0) {
+            // If the device is in landscape and the page is shorter
+            // than the height of the view, stretch the thumbnail to fill the
+            // space.
+            scaleFactorY = (float) getDesiredThumbnailHeight(this) /
+                    (float)thumbnailHeight;
+        } else {
+            // In the portrait case, this looks nice.
+            scaleFactorY = scaleFactorX;
         }
+
+        canvas.scale(scaleFactorX, scaleFactorY);
+
         thumbnail.draw(canvas);
         return bm;
     }
 
     // -------------------------------------------------------------------------
-    // WebViewClient implementation.
+    // Helper function for WebViewClient.
     //-------------------------------------------------------------------------
 
     // Use in overrideUrlLoading
@@ -2501,1022 +2426,320 @@ public class BrowserActivity extends Activity
     /* package */ final static String SCHEME_WTAI_SD = "wtai://wp/sd;";
     /* package */ final static String SCHEME_WTAI_AP = "wtai://wp/ap;";
 
-    /* package */ WebViewClient getWebViewClient() {
-        return mWebViewClient;
-    }
-
-    private void updateIcon(WebView view, Bitmap icon) {
-        if (icon != null) {
-            BrowserBookmarksAdapter.updateBookmarkFavicon(mResolver,
-                    view.getOriginalUrl(), view.getUrl(), icon);
+    // Keep this initial progress in sync with initialProgressValue (* 100)
+    // in ProgressTracker.cpp
+    private final static int INITIAL_PROGRESS = 10;
+
+    void onPageStarted(WebView view, String url, Bitmap favicon) {
+        // when BrowserActivity just starts, onPageStarted may be called before
+        // onResume as it is triggered from onCreate. Call resumeWebViewTimers
+        // to start the timer. As we won't switch tabs while an activity is in
+        // pause state, we can ensure calling resume and pause in pair.
+        if (mActivityInPause) resumeWebViewTimers();
+
+        resetLockIcon(url);
+        setUrlTitle(url, null);
+        setFavicon(favicon);
+        // Show some progress so that the user knows the page is beginning to
+        // load
+        onProgressChanged(view, INITIAL_PROGRESS);
+        mDidStopLoad = false;
+        if (!mIsNetworkUp) createAndShowNetworkDialog();
+
+        if (mSettings.isTracing()) {
+            String host;
+            try {
+                WebAddress uri = new WebAddress(url);
+                host = uri.mHost;
+            } catch (android.net.ParseException ex) {
+                host = "browser";
+            }
+            host = host.replace('.', '_');
+            host += ".trace";
+            mInTrace = true;
+            Debug.startMethodTracing(host, 20 * 1024 * 1024);
         }
-        setFavicon(icon);
-    }
 
-    private void updateIcon(String url, Bitmap icon) {
-        if (icon != null) {
-            BrowserBookmarksAdapter.updateBookmarkFavicon(mResolver,
-                    null, url, icon);
+        // Performance probe
+        if (false) {
+            mStart = SystemClock.uptimeMillis();
+            mProcessStart = Process.getElapsedCpuTime();
+            long[] sysCpu = new long[7];
+            if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
+                    sysCpu, null)) {
+                mUserStart = sysCpu[0] + sysCpu[1];
+                mSystemStart = sysCpu[2];
+                mIdleStart = sysCpu[3];
+                mIrqStart = sysCpu[4] + sysCpu[5] + sysCpu[6];
+            }
+            mUiStart = SystemClock.currentThreadTimeMillis();
         }
-        setFavicon(icon);
     }
 
-    private final WebViewClient mWebViewClient = new WebViewClient() {
-        @Override
-        public void onPageStarted(WebView view, String url, Bitmap favicon) {
-            resetLockIcon(url);
-            setUrlTitle(url, null);
-
-            // We've started to load a new page. If there was a pending message
-            // to save a screenshot then we will now take the new page and
-            // save an incorrect screenshot. Therefore, remove any pending
-            // thumbnail messages from the queue.
-            mHandler.removeMessages(UPDATE_BOOKMARK_THUMBNAIL);
-
-            // If we start a touch icon load and then load a new page, we don't
-            // want to cancel the current touch icon loader. But, we do want to
-            // create a new one when the touch icon url is known.
-            if (mTouchIconLoader != null) {
-                mTouchIconLoader.mActivity = null;
-                mTouchIconLoader = null;
-            }
-
-            ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(false);
-            if (errorConsole != null) {
-                errorConsole.clearErrorMessages();
-                if (mShouldShowErrorConsole) {
-                    errorConsole.showConsole(ErrorConsoleView.SHOW_NONE);
-                }
-            }
-
-            // Call updateIcon instead of setFavicon so the bookmark
-            // database can be updated.
-            updateIcon(url, favicon);
-
-            if (mSettings.isTracing()) {
-                String host;
-                try {
-                    WebAddress uri = new WebAddress(url);
-                    host = uri.mHost;
-                } catch (android.net.ParseException ex) {
-                    host = "browser";
-                }
-                host = host.replace('.', '_');
-                host += ".trace";
-                mInTrace = true;
-                Debug.startMethodTracing(host, 20 * 1024 * 1024);
-            }
-
-            // Performance probe
-            if (false) {
-                mStart = SystemClock.uptimeMillis();
-                mProcessStart = Process.getElapsedCpuTime();
-                long[] sysCpu = new long[7];
-                if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
-                        sysCpu, null)) {
-                    mUserStart = sysCpu[0] + sysCpu[1];
-                    mSystemStart = sysCpu[2];
-                    mIdleStart = sysCpu[3];
-                    mIrqStart = sysCpu[4] + sysCpu[5] + sysCpu[6];
-                }
-                mUiStart = SystemClock.currentThreadTimeMillis();
-            }
-
-            if (!mPageStarted) {
-                mPageStarted = true;
-                // if onResume() has been called, resumeWebViewTimers() does
-                // nothing.
-                resumeWebViewTimers();
-            }
-
-            // reset sync timer to avoid sync starts during loading a page
-            CookieSyncManager.getInstance().resetSync();
-
-            mInLoad = true;
-            mDidStopLoad = false;
-            showFakeTitleBar();
-            updateInLoadMenuItems();
-            if (!mIsNetworkUp) {
-                createAndShowNetworkDialog();
-                if (view != null) {
-                    view.setNetworkAvailable(false);
+    void onPageFinished(WebView view, String url) {
+        // Reset the title and icon in case we stopped a provisional load.
+        resetTitleAndIcon(view);
+        // Update the lock icon image only once we are done loading
+        updateLockIconToLatest();
+        // pause the WebView timer and release the wake lock if it is finished
+        // while BrowserActivity is in pause state.
+        if (mActivityInPause && pauseWebViewTimers()) {
+            if (mWakeLock.isHeld()) {
+                mHandler.removeMessages(RELEASE_WAKELOCK);
+                mWakeLock.release();
+            }
+        }
+
+        // Performance probe
+        if (false) {
+            long[] sysCpu = new long[7];
+            if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
+                    sysCpu, null)) {
+                String uiInfo = "UI thread used "
+                        + (SystemClock.currentThreadTimeMillis() - mUiStart)
+                        + " ms";
+                if (LOGD_ENABLED) {
+                    Log.d(LOGTAG, uiInfo);
+                }
+                //The string that gets written to the log
+                String performanceString = "It took total "
+                        + (SystemClock.uptimeMillis() - mStart)
+                        + " ms clock time to load the page."
+                        + "\nbrowser process used "
+                        + (Process.getElapsedCpuTime() - mProcessStart)
+                        + " ms, user processes used "
+                        + (sysCpu[0] + sysCpu[1] - mUserStart) * 10
+                        + " ms, kernel used "
+                        + (sysCpu[2] - mSystemStart) * 10
+                        + " ms, idle took " + (sysCpu[3] - mIdleStart) * 10
+                        + " ms and irq took "
+                        + (sysCpu[4] + sysCpu[5] + sysCpu[6] - mIrqStart)
+                        * 10 + " ms, " + uiInfo;
+                if (LOGD_ENABLED) {
+                    Log.d(LOGTAG, performanceString + "\nWebpage: " + url);
                 }
-            }
-        }
-
-        @Override
-        public void onPageFinished(WebView view, String url) {
-            // Reset the title and icon in case we stopped a provisional
-            // load.
-            resetTitleAndIcon(view);
-
-            if (!mDidStopLoad) {
-                // Only update the bookmark screenshot if the user did not
-                // cancel the load early.
-                Message updateScreenshot = Message.obtain(mHandler, UPDATE_BOOKMARK_THUMBNAIL, view);
-                mHandler.sendMessageDelayed(updateScreenshot, 500);
-            }
-
-            // Update the lock icon image only once we are done loading
-            updateLockIconToLatest();
-
-            // Performance probe
-            if (false) {
-                long[] sysCpu = new long[7];
-                if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
-                        sysCpu, null)) {
-                    String uiInfo = "UI thread used "
-                            + (SystemClock.currentThreadTimeMillis() - mUiStart)
-                            + " ms";
-                    if (LOGD_ENABLED) {
-                        Log.d(LOGTAG, uiInfo);
+                if (url != null) {
+                    // strip the url to maintain consistency
+                    String newUrl = new String(url);
+                    if (newUrl.startsWith("http://www.")) {
+                        newUrl = newUrl.substring(11);
+                    } else if (newUrl.startsWith("http://")) {
+                        newUrl = newUrl.substring(7);
+                    } else if (newUrl.startsWith("https://www.")) {
+                        newUrl = newUrl.substring(12);
+                    } else if (newUrl.startsWith("https://")) {
+                        newUrl = newUrl.substring(8);
                     }
-                    //The string that gets written to the log
-                    String performanceString = "It took total "
-                            + (SystemClock.uptimeMillis() - mStart)
-                            + " ms clock time to load the page."
-                            + "\nbrowser process used "
-                            + (Process.getElapsedCpuTime() - mProcessStart)
-                            + " ms, user processes used "
-                            + (sysCpu[0] + sysCpu[1] - mUserStart) * 10
-                            + " ms, kernel used "
-                            + (sysCpu[2] - mSystemStart) * 10
-                            + " ms, idle took " + (sysCpu[3] - mIdleStart) * 10
-                            + " ms and irq took "
-                            + (sysCpu[4] + sysCpu[5] + sysCpu[6] - mIrqStart)
-                            * 10 + " ms, " + uiInfo;
                     if (LOGD_ENABLED) {
-                        Log.d(LOGTAG, performanceString + "\nWebpage: " + url);
-                    }
-                    if (url != null) {
-                        // strip the url to maintain consistency
-                        String newUrl = new String(url);
-                        if (newUrl.startsWith("http://www.")) {
-                            newUrl = newUrl.substring(11);
-                        } else if (newUrl.startsWith("http://")) {
-                            newUrl = newUrl.substring(7);
-                        } else if (newUrl.startsWith("https://www.")) {
-                            newUrl = newUrl.substring(12);
-                        } else if (newUrl.startsWith("https://")) {
-                            newUrl = newUrl.substring(8);
-                        }
-                        if (LOGD_ENABLED) {
-                            Log.d(LOGTAG, newUrl + " loaded");
-                        }
-                        /*
-                        if (sWhiteList.contains(newUrl)) {
-                            // The string that gets pushed to the statistcs
-                            // service
-                            performanceString = performanceString
-                                    + "\nWebpage: "
-                                    + newUrl
-                                    + "\nCarrier: "
-                                    + android.os.SystemProperties
-                                            .get("gsm.sim.operator.alpha");
-                            if (mWebView != null
-                                    && mWebView.getContext() != null
-                                    && mWebView.getContext().getSystemService(
-                                    Context.CONNECTIVITY_SERVICE) != null) {
-                                ConnectivityManager cManager =
-                                        (ConnectivityManager) mWebView
-                                        .getContext().getSystemService(
-                                        Context.CONNECTIVITY_SERVICE);
-                                NetworkInfo nInfo = cManager
-                                        .getActiveNetworkInfo();
-                                if (nInfo != null) {
-                                    performanceString = performanceString
-                                            + "\nNetwork Type: "
-                                            + nInfo.getType().toString();
-                                }
-                            }
-                            Checkin.logEvent(mResolver,
-                                    Checkin.Events.Tag.WEBPAGE_LOAD,
-                                    performanceString);
-                            Log.w(LOGTAG, "pushed to the statistics service");
-                        }
-                        */
+                        Log.d(LOGTAG, newUrl + " loaded");
                     }
                 }
-             }
-
-            if (mInTrace) {
-                mInTrace = false;
-                Debug.stopMethodTracing();
             }
+         }
 
-            if (mPageStarted) {
-                mPageStarted = false;
-                // pauseWebViewTimers() will do nothing and return false if
-                // onPause() is not called yet.
-                if (pauseWebViewTimers()) {
-                    if (mWakeLock.isHeld()) {
-                        mHandler.removeMessages(RELEASE_WAKELOCK);
-                        mWakeLock.release();
-                    }
-                }
-            }
+        if (mInTrace) {
+            mInTrace = false;
+            Debug.stopMethodTracing();
         }
+    }
 
-        // return true if want to hijack the url to let another app to handle it
-        @Override
-        public boolean shouldOverrideUrlLoading(WebView view, String url) {
-            if (url.startsWith(SCHEME_WTAI)) {
-                // wtai://wp/mc;number
-                // number=string(phone-number)
-                if (url.startsWith(SCHEME_WTAI_MC)) {
-                    Intent intent = new Intent(Intent.ACTION_VIEW,
-                            Uri.parse(WebView.SCHEME_TEL +
-                            url.substring(SCHEME_WTAI_MC.length())));
-                    startActivity(intent);
-                    return true;
-                }
-                // wtai://wp/sd;dtmf
-                // dtmf=string(dialstring)
-                if (url.startsWith(SCHEME_WTAI_SD)) {
-                    // TODO
-                    // only send when there is active voice connection
-                    return false;
-                }
-                // wtai://wp/ap;number;name
-                // number=string(phone-number)
-                // name=string
-                if (url.startsWith(SCHEME_WTAI_AP)) {
-                    // TODO
-                    return false;
-                }
+    boolean shouldOverrideUrlLoading(WebView view, String url) {
+        if (url.startsWith(SCHEME_WTAI)) {
+            // wtai://wp/mc;number
+            // number=string(phone-number)
+            if (url.startsWith(SCHEME_WTAI_MC)) {
+                Intent intent = new Intent(Intent.ACTION_VIEW,
+                        Uri.parse(WebView.SCHEME_TEL +
+                        url.substring(SCHEME_WTAI_MC.length())));
+                startActivity(intent);
+                return true;
             }
-
-            // The "about:" schemes are internal to the browser; don't
-            // want these to be dispatched to other apps.
-            if (url.startsWith("about:")) {
+            // wtai://wp/sd;dtmf
+            // dtmf=string(dialstring)
+            if (url.startsWith(SCHEME_WTAI_SD)) {
+                // TODO: only send when there is active voice connection
                 return false;
             }
-
-            Intent intent;
-
-            // perform generic parsing of the URI to turn it into an Intent.
-            try {
-                intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
-            } catch (URISyntaxException ex) {
-                Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
+            // wtai://wp/ap;number;name
+            // number=string(phone-number)
+            // name=string
+            if (url.startsWith(SCHEME_WTAI_AP)) {
+                // TODO
                 return false;
             }
-
-            // check whether the intent can be resolved. If not, we will see
-            // whether we can download it from the Market.
-            if (getPackageManager().resolveActivity(intent, 0) == null) {
-                String packagename = intent.getPackage();
-                if (packagename != null) {
-                    intent = new Intent(Intent.ACTION_VIEW, Uri
-                            .parse("market://search?q=pname:" + packagename));
-                    intent.addCategory(Intent.CATEGORY_BROWSABLE);
-                    startActivity(intent);
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-
-            // sanitize the Intent, ensuring web pages can not bypass browser
-            // security (only access to BROWSABLE activities).
-            intent.addCategory(Intent.CATEGORY_BROWSABLE);
-            intent.setComponent(null);
-            try {
-                if (startActivityIfNeeded(intent, -1)) {
-                    return true;
-                }
-            } catch (ActivityNotFoundException ex) {
-                // ignore the error. If no application can handle the URL,
-                // eg about:blank, assume the browser can handle it.
-            }
-
-            if (mMenuIsDown) {
-                openTab(url);
-                closeOptionsMenu();
-                return true;
-            }
-
-            return false;
-        }
-
-        /**
-         * Updates the lock icon. This method is called when we discover another
-         * resource to be loaded for this page (for example, javascript). While
-         * we update the icon type, we do not update the lock icon itself until
-         * we are done loading, it is slightly more secure this way.
-         */
-        @Override
-        public void onLoadResource(WebView view, String url) {
-            if (url != null && url.length() > 0) {
-                // It is only if the page claims to be secure
-                // that we may have to update the lock:
-                if (mLockIconType == LOCK_ICON_SECURE) {
-                    // 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 (LOGV_ENABLED) {
-                            Log.v(LOGTAG, "BrowserActivity.updateLockIcon:" +
-                                  " updated lock icon to " + mLockIconType + " due to " + url);
-                        }
-                    }
-                }
-            }
-        }
-
-        /**
-         * Show the dialog, asking the user if they would like to continue after
-         * an excessive number of HTTP redirects.
-         */
-        @Override
-        public void onTooManyRedirects(WebView view, final Message cancelMsg,
-                final Message continueMsg) {
-            new AlertDialog.Builder(BrowserActivity.this)
-                .setTitle(R.string.browserFrameRedirect)
-                .setMessage(R.string.browserFrame307Post)
-                .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int which) {
-                        continueMsg.sendToTarget();
-                    }})
-                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int which) {
-                        cancelMsg.sendToTarget();
-                    }})
-                .setOnCancelListener(new OnCancelListener() {
-                    public void onCancel(DialogInterface dialog) {
-                        cancelMsg.sendToTarget();
-                    }})
-                .show();
-        }
-
-        // Container class for the next error dialog that needs to be
-        // displayed.
-        class ErrorDialog {
-            public final int mTitle;
-            public final String mDescription;
-            public final int mError;
-            ErrorDialog(int title, String desc, int error) {
-                mTitle = title;
-                mDescription = desc;
-                mError = error;
-            }
-        };
-
-        private void processNextError() {
-            if (mQueuedErrors == null) {
-                return;
-            }
-            // The first one is currently displayed so just remove it.
-            mQueuedErrors.removeFirst();
-            if (mQueuedErrors.size() == 0) {
-                mQueuedErrors = null;
-                return;
-            }
-            showError(mQueuedErrors.getFirst());
-        }
-
-        private DialogInterface.OnDismissListener mDialogListener =
-                new DialogInterface.OnDismissListener() {
-                    public void onDismiss(DialogInterface d) {
-                        processNextError();
-                    }
-                };
-        private LinkedList<ErrorDialog> mQueuedErrors;
-
-        private void queueError(int err, String desc) {
-            if (mQueuedErrors == null) {
-                mQueuedErrors = new LinkedList<ErrorDialog>();
-            }
-            for (ErrorDialog d : mQueuedErrors) {
-                if (d.mError == err) {
-                    // Already saw a similar error, ignore the new one.
-                    return;
-                }
-            }
-            ErrorDialog errDialog = new ErrorDialog(
-                    err == WebViewClient.ERROR_FILE_NOT_FOUND ?
-                    R.string.browserFrameFileErrorLabel :
-                    R.string.browserFrameNetworkErrorLabel,
-                    desc, err);
-            mQueuedErrors.addLast(errDialog);
-
-            // Show the dialog now if the queue was empty.
-            if (mQueuedErrors.size() == 1) {
-                showError(errDialog);
-            }
-        }
-
-        private void showError(ErrorDialog errDialog) {
-            AlertDialog d = new AlertDialog.Builder(BrowserActivity.this)
-                    .setTitle(errDialog.mTitle)
-                    .setMessage(errDialog.mDescription)
-                    .setPositiveButton(R.string.ok, null)
-                    .create();
-            d.setOnDismissListener(mDialogListener);
-            d.show();
-        }
-
-        /**
-         * Show a dialog informing the user of the network error reported by
-         * WebCore.
-         */
-        @Override
-        public void onReceivedError(WebView view, int errorCode,
-                String description, String failingUrl) {
-            if (errorCode != WebViewClient.ERROR_HOST_LOOKUP &&
-                    errorCode != WebViewClient.ERROR_CONNECT &&
-                    errorCode != WebViewClient.ERROR_BAD_URL &&
-                    errorCode != WebViewClient.ERROR_UNSUPPORTED_SCHEME &&
-                    errorCode != WebViewClient.ERROR_FILE) {
-                queueError(errorCode, description);
-            }
-            Log.e(LOGTAG, "onReceivedError " + errorCode + " " + failingUrl
-                    + " " + description);
-
-            // We need to reset the title after an error.
-            resetTitleAndRevertLockIcon();
-        }
-
-        /**
-         * Check with the user if it is ok to resend POST data as the page they
-         * are trying to navigate to is the result of a POST.
-         */
-        @Override
-        public void onFormResubmission(WebView view, final Message dontResend,
-                                       final Message resend) {
-            new AlertDialog.Builder(BrowserActivity.this)
-                .setTitle(R.string.browserFrameFormResubmitLabel)
-                .setMessage(R.string.browserFrameFormResubmitMessage)
-                .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int which) {
-                        resend.sendToTarget();
-                    }})
-                .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int which) {
-                        dontResend.sendToTarget();
-                    }})
-                .setOnCancelListener(new OnCancelListener() {
-                    public void onCancel(DialogInterface dialog) {
-                        dontResend.sendToTarget();
-                    }})
-                .show();
         }
 
-        /**
-         * Insert the url into the visited history database.
-         * @param url The url to be inserted.
-         * @param isReload True if this url is being reloaded.
-         * FIXME: Not sure what to do when reloading the page.
-         */
-        @Override
-        public void doUpdateVisitedHistory(WebView view, String url,
-                boolean isReload) {
-            if (url.regionMatches(true, 0, "about:", 0, 6)) {
-                return;
-            }
-            // remove "client" before updating it to the history so that it wont
-            // show up in the auto-complete list.
-            int index = url.indexOf("client=ms-");
-            if (index > 0 && url.contains(".google.")) {
-                int end = url.indexOf('&', index);
-                if (end > 0) {
-                    url = url.substring(0, index)
-                            .concat(url.substring(end + 1));
-                } else {
-                    // the url.charAt(index-1) should be either '?' or '&'
-                    url = url.substring(0, index-1);
-                }
-            }
-            Browser.updateVisitedHistory(mResolver, url, true);
-            WebIconDatabase.getInstance().retainIconForPageUrl(url);
-        }
-
-        /**
-         * Displays SSL error(s) dialog to the user.
-         */
-        @Override
-        public void onReceivedSslError(
-            final WebView view, final SslErrorHandler handler, final SslError error) {
-
-            if (mSettings.showSecurityWarnings()) {
-                final LayoutInflater factory =
-                    LayoutInflater.from(BrowserActivity.this);
-                final View warningsView =
-                    factory.inflate(R.layout.ssl_warnings, null);
-                final LinearLayout placeholder =
-                    (LinearLayout)warningsView.findViewById(R.id.placeholder);
-
-                if (error.hasError(SslError.SSL_UNTRUSTED)) {
-                    LinearLayout ll = (LinearLayout)factory
-                        .inflate(R.layout.ssl_warning, null);
-                    ((TextView)ll.findViewById(R.id.warning))
-                        .setText(R.string.ssl_untrusted);
-                    placeholder.addView(ll);
-                }
-
-                if (error.hasError(SslError.SSL_IDMISMATCH)) {
-                    LinearLayout ll = (LinearLayout)factory
-                        .inflate(R.layout.ssl_warning, null);
-                    ((TextView)ll.findViewById(R.id.warning))
-                        .setText(R.string.ssl_mismatch);
-                    placeholder.addView(ll);
-                }
-
-                if (error.hasError(SslError.SSL_EXPIRED)) {
-                    LinearLayout ll = (LinearLayout)factory
-                        .inflate(R.layout.ssl_warning, null);
-                    ((TextView)ll.findViewById(R.id.warning))
-                        .setText(R.string.ssl_expired);
-                    placeholder.addView(ll);
-                }
-
-                if (error.hasError(SslError.SSL_NOTYETVALID)) {
-                    LinearLayout ll = (LinearLayout)factory
-                        .inflate(R.layout.ssl_warning, null);
-                    ((TextView)ll.findViewById(R.id.warning))
-                        .setText(R.string.ssl_not_yet_valid);
-                    placeholder.addView(ll);
-                }
-
-                new AlertDialog.Builder(BrowserActivity.this)
-                    .setTitle(R.string.security_warning)
-                    .setIcon(android.R.drawable.ic_dialog_alert)
-                    .setView(warningsView)
-                    .setPositiveButton(R.string.ssl_continue,
-                            new DialogInterface.OnClickListener() {
-                                public void onClick(DialogInterface dialog, int whichButton) {
-                                    handler.proceed();
-                                }
-                            })
-                    .setNeutralButton(R.string.view_certificate,
-                            new DialogInterface.OnClickListener() {
-                                public void onClick(DialogInterface dialog, int whichButton) {
-                                    showSSLCertificateOnError(view, handler, error);
-                                }
-                            })
-                    .setNegativeButton(R.string.cancel,
-                            new DialogInterface.OnClickListener() {
-                                public void onClick(DialogInterface dialog, int whichButton) {
-                                    handler.cancel();
-                                    BrowserActivity.this.resetTitleAndRevertLockIcon();
-                                }
-                            })
-                    .setOnCancelListener(
-                            new DialogInterface.OnCancelListener() {
-                                public void onCancel(DialogInterface dialog) {
-                                    handler.cancel();
-                                    BrowserActivity.this.resetTitleAndRevertLockIcon();
-                                }
-                            })
-                    .show();
-            } else {
-                handler.proceed();
-            }
+        // The "about:" schemes are internal to the browser; don't want these to
+        // be dispatched to other apps.
+        if (url.startsWith("about:")) {
+            return false;
         }
 
-        /**
-         * Handles an HTTP authentication request.
-         *
-         * @param handler The authentication handler
-         * @param host The host
-         * @param realm The realm
-         */
-        @Override
-        public void onReceivedHttpAuthRequest(WebView view,
-                final HttpAuthHandler handler, final String host, final String realm) {
-            String username = null;
-            String password = null;
-
-            boolean reuseHttpAuthUsernamePassword =
-                handler.useHttpAuthUsernamePassword();
-
-            if (reuseHttpAuthUsernamePassword &&
-                    (mTabControl.getCurrentWebView() != null)) {
-                String[] credentials =
-                        mTabControl.getCurrentWebView()
-                                .getHttpAuthUsernamePassword(host, realm);
-                if (credentials != null && credentials.length == 2) {
-                    username = credentials[0];
-                    password = credentials[1];
-                }
-            }
-
-            if (username != null && password != null) {
-                handler.proceed(username, password);
-            } else {
-                showHttpAuthentication(handler, host, realm, null, null, null, 0);
-            }
+        Intent intent;
+        // perform generic parsing of the URI to turn it into an Intent.
+        try {
+            intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
+        } catch (URISyntaxException ex) {
+            Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
+            return false;
         }
 
-        @Override
-        public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
-            if (mMenuIsDown) {
-                // only check shortcut key when MENU is held
-                return getWindow().isShortcutKey(event.getKeyCode(), event);
+        // check whether the intent can be resolved. If not, we will see
+        // whether we can download it from the Market.
+        if (getPackageManager().resolveActivity(intent, 0) == null) {
+            String packagename = intent.getPackage();
+            if (packagename != null) {
+                intent = new Intent(Intent.ACTION_VIEW, Uri
+                        .parse("market://search?q=pname:" + packagename));
+                intent.addCategory(Intent.CATEGORY_BROWSABLE);
+                startActivity(intent);
+                return true;
             } else {
                 return false;
             }
         }
 
-        @Override
-        public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
-            if (view != mTabControl.getCurrentTopWebView()) {
-                return;
-            }
-            if (event.isDown()) {
-                BrowserActivity.this.onKeyDown(event.getKeyCode(), event);
-            } else {
-                BrowserActivity.this.onKeyUp(event.getKeyCode(), event);
-            }
-        }
-    };
-
-    //--------------------------------------------------------------------------
-    // WebChromeClient implementation
-    //--------------------------------------------------------------------------
-
-    /* package */ WebChromeClient getWebChromeClient() {
-        return mWebChromeClient;
-    }
-
-    private final WebChromeClient mWebChromeClient = new WebChromeClient() {
-        // Helper method to create a new tab or sub window.
-        private void createWindow(final boolean dialog, final Message msg) {
-            if (dialog) {
-                mTabControl.createSubWindow();
-                final TabControl.Tab t = mTabControl.getCurrentTab();
-                attachSubWindow(t);
-                WebView.WebViewTransport transport =
-                        (WebView.WebViewTransport) msg.obj;
-                transport.setWebView(t.getSubWebView());
-                msg.sendToTarget();
-            } else {
-                final TabControl.Tab parent = mTabControl.getCurrentTab();
-                final TabControl.Tab newTab
-                        = openTabAndShow(EMPTY_URL_DATA, false, null);
-                if (newTab != parent) {
-                    parent.addChildTab(newTab);
-                }
-                WebView.WebViewTransport transport =
-                        (WebView.WebViewTransport) msg.obj;
-                transport.setWebView(mTabControl.getCurrentWebView());
-                msg.sendToTarget();
-            }
-        }
-
-        @Override
-        public boolean onCreateWindow(WebView view, final boolean dialog,
-                final boolean userGesture, final Message resultMsg) {
-            // Short-circuit if we can't create any more tabs or sub windows.
-            if (dialog && mTabControl.getCurrentSubWindow() != null) {
-                new AlertDialog.Builder(BrowserActivity.this)
-                        .setTitle(R.string.too_many_subwindows_dialog_title)
-                        .setIcon(android.R.drawable.ic_dialog_alert)
-                        .setMessage(R.string.too_many_subwindows_dialog_message)
-                        .setPositiveButton(R.string.ok, null)
-                        .show();
-                return false;
-            } else if (mTabControl.getTabCount() >= TabControl.MAX_TABS) {
-                new AlertDialog.Builder(BrowserActivity.this)
-                        .setTitle(R.string.too_many_windows_dialog_title)
-                        .setIcon(android.R.drawable.ic_dialog_alert)
-                        .setMessage(R.string.too_many_windows_dialog_message)
-                        .setPositiveButton(R.string.ok, null)
-                        .show();
-                return false;
-            }
-
-            // Short-circuit if this was a user gesture.
-            if (userGesture) {
-                createWindow(dialog, resultMsg);
+        // sanitize the Intent, ensuring web pages can not bypass browser
+        // security (only access to BROWSABLE activities).
+        intent.addCategory(Intent.CATEGORY_BROWSABLE);
+        intent.setComponent(null);
+        try {
+            if (startActivityIfNeeded(intent, -1)) {
                 return true;
             }
-
-            // Allow the popup and create the appropriate window.
-            final AlertDialog.OnClickListener allowListener =
-                    new AlertDialog.OnClickListener() {
-                        public void onClick(DialogInterface d,
-                                int which) {
-                            createWindow(dialog, resultMsg);
-                        }
-                    };
-
-            // Block the popup by returning a null WebView.
-            final AlertDialog.OnClickListener blockListener =
-                    new AlertDialog.OnClickListener() {
-                        public void onClick(DialogInterface d, int which) {
-                            resultMsg.sendToTarget();
-                        }
-                    };
-
-            // Build a confirmation dialog to display to the user.
-            final AlertDialog d =
-                    new AlertDialog.Builder(BrowserActivity.this)
-                    .setTitle(R.string.attention)
-                    .setIcon(android.R.drawable.ic_dialog_alert)
-                    .setMessage(R.string.popup_window_attempt)
-                    .setPositiveButton(R.string.allow, allowListener)
-                    .setNegativeButton(R.string.block, blockListener)
-                    .setCancelable(false)
-                    .create();
-
-            // Show the confirmation dialog.
-            d.show();
-            return true;
+        } catch (ActivityNotFoundException ex) {
+            // ignore the error. If no application can handle the URL,
+            // eg about:blank, assume the browser can handle it.
         }
 
-        @Override
-        public void onCloseWindow(WebView window) {
-            final TabControl.Tab current = mTabControl.getCurrentTab();
-            final TabControl.Tab parent = current.getParentTab();
-            if (parent != null) {
-                // JavaScript can only close popup window.
-                switchToTab(mTabControl.getTabIndex(parent));
-                // Now we need to close the window
-                closeTab(current);
-            }
+        if (mMenuIsDown) {
+            openTab(url);
+            closeOptionsMenu();
+            return true;
         }
+        return false;
+    }
 
-        @Override
-        public void onProgressChanged(WebView view, int newProgress) {
-            mTitleBar.setProgress(newProgress);
-            if (mFakeTitleBar != null) {
-                mFakeTitleBar.setProgress(newProgress);
-            }
+    // -------------------------------------------------------------------------
+    // Helper function for WebChromeClient
+    // -------------------------------------------------------------------------
 
-            if (newProgress == 100) {
-                // onProgressChanged() may continue to be called after the main
-                // frame has finished loading, as any remaining sub frames
-                // continue to load. We'll only get called once though with
-                // newProgress as 100 when everything is loaded.
-                // (onPageFinished is called once when the main frame completes
-                // loading regardless of the state of any sub frames so calls
-                // to onProgressChanges may continue after onPageFinished has
-                // executed)
-
-                // sync cookies and cache promptly here.
-                CookieSyncManager.getInstance().sync();
-                if (mInLoad) {
-                    mInLoad = false;
-                    updateInLoadMenuItems();
-                    // If the options menu is open, leave the title bar
-                    if (!mOptionsMenuOpen || !mIconView) {
-                        hideFakeTitleBar();
-                    }
-                }
-            } else if (!mInLoad) {
-                // onPageFinished may have already been called but a subframe
-                // is still loading and updating the progress. Reset mInLoad
-                // and update the menu items.
-                mInLoad = true;
+    void onProgressChanged(WebView view, int newProgress) {
+        mFakeTitleBar.setProgress(newProgress);
+
+        if (newProgress == 100) {
+            // onProgressChanged() may continue to be called after the main
+            // frame has finished loading, as any remaining sub frames continue
+            // to load. We'll only get called once though with newProgress as
+            // 100 when everything is loaded. (onPageFinished is called once
+            // when the main frame completes loading regardless of the state of
+            // any sub frames so calls to onProgressChanges may continue after
+            // onPageFinished has executed)
+            if (mInLoad) {
+                mInLoad = false;
                 updateInLoadMenuItems();
-                if (!mOptionsMenuOpen || mIconView) {
-                    // This page has begun to load, so show the title bar
-                    showFakeTitleBar();
+                // If the options menu is open, leave the title bar
+                if (!mOptionsMenuOpen || !mIconView) {
+                    hideFakeTitleBar();
                 }
             }
-        }
-
-        @Override
-        public void onReceivedTitle(WebView view, String title) {
-            String url = view.getUrl();
-
-            // here, if url is null, we want to reset the title
-            setUrlTitle(url, title);
-
-            if (url == null ||
-                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://")) {
-                url = url.substring(4);
-            }
-            try {
-                url = "%" + url;
-                String [] selArgs = new String[] { url };
-
-                String where = Browser.BookmarkColumns.URL + " LIKE ? AND "
-                        + Browser.BookmarkColumns.BOOKMARK + " = 0";
-                Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
-                    Browser.HISTORY_PROJECTION, where, selArgs, null);
-                if (c.moveToFirst()) {
-                    // Current implementation of database only has one entry per
-                    // url.
-                    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) {
-                Log.e(LOGTAG, "BrowserActivity onReceived title", e);
-            } catch (SQLiteException ex) {
-                Log.e(LOGTAG, "onReceivedTitle() caught SQLiteException: ", ex);
+        } else {
+            if (!mInLoad) {
+                // onPageFinished may have already been called but a subframe is
+                // still loading and updating the progress. Reset mInLoad and
+                // update the menu items.
+                mInLoad = true;
+                updateInLoadMenuItems();
             }
-        }
-
-        @Override
-        public void onReceivedIcon(WebView view, Bitmap icon) {
-            updateIcon(view, icon);
-        }
-
-        @Override
-        public void onReceivedTouchIconUrl(WebView view, String url,
-                boolean precomposed) {
-            final ContentResolver cr = getContentResolver();
-            final Cursor c =
-                    BrowserBookmarksAdapter.queryBookmarksForUrl(cr,
-                            view.getOriginalUrl(), view.getUrl(), true);
-            if (c != null) {
-                if (c.getCount() > 0) {
-                    // Let precomposed icons take precedence over non-composed
-                    // icons.
-                    if (precomposed && mTouchIconLoader != null) {
-                        mTouchIconLoader.cancel(false);
-                        mTouchIconLoader = null;
-                    }
-                    // Have only one async task at a time.
-                    if (mTouchIconLoader == null) {
-                        mTouchIconLoader = new DownloadTouchIcon(
-                                BrowserActivity.this, cr, c, view);
-                        mTouchIconLoader.execute(url);
-                    }
-                } else {
-                    c.close();
-                }
+            // When the page first begins to load, the Activity may still be
+            // paused, in which case showFakeTitleBar will do nothing.  Call
+            // again as the page continues to load so that it will be shown.
+            // (Calling it will the fake title bar is already showing will also
+            // do nothing.
+            if (!mOptionsMenuOpen || mIconView) {
+                // This page has begun to load, so show the title bar
+                showFakeTitleBar();
             }
         }
+    }
 
-        @Override
-        public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
-            if (mCustomView != null)
-                return;
-
-            // Add the custom view to its container.
-            mCustomViewContainer.addView(view, COVER_SCREEN_GRAVITY_CENTER);
-            mCustomView = view;
-            mCustomViewCallback = callback;
-            // Save the menu state and set it to empty while the custom
-            // view is showing.
-            mOldMenuState = mMenuState;
-            mMenuState = EMPTY_MENU;
-            // Hide the content view.
-            mContentView.setVisibility(View.GONE);
-            // Finally show the custom view container.
-            mCustomViewContainer.setVisibility(View.VISIBLE);
-            mCustomViewContainer.bringToFront();
-        }
-
-        @Override
-        public void onHideCustomView() {
-            if (mCustomView == null)
-                return;
-
-            // Hide the custom view.
-            mCustomView.setVisibility(View.GONE);
-            // Remove the custom view from its container.
-            mCustomViewContainer.removeView(mCustomView);
-            mCustomView = null;
-            // Reset the old menu state.
-            mMenuState = mOldMenuState;
-            mOldMenuState = EMPTY_MENU;
-            mCustomViewContainer.setVisibility(View.GONE);
-            mCustomViewCallback.onCustomViewHidden();
-            // Show the content view.
-            mContentView.setVisibility(View.VISIBLE);
-        }
-
-        /**
-         * The origin has exceeded its 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 estimatedSize the estimated size of the database.
-         * @param totalUsedQuota is the sum of all origins' quota.
-         * @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, long estimatedSize,
-            long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
-            mSettings.getWebStorageSizeManager().onExceededDatabaseQuota(
-                    url, databaseIdentifier, currentQuota, estimatedSize,
-                    totalUsedQuota, quotaUpdater);
-        }
-
-        /**
-         * The Application Cache has exceeded its max size.
-         * @param spaceNeeded is the amount of disk space that would be needed
-         * in order for the last appcache operation to succeed.
-         * @param totalUsedQuota is the sum of all origins' quota.
-         * @param quotaUpdater A callback to inform the WebCore thread that a new
-         * app cache size is available. This callback must always be executed at
-         * some point to ensure that the sleeping WebCore thread is woken up.
-         */
-        @Override
-        public void onReachedMaxAppCacheSize(long spaceNeeded,
-                long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
-            mSettings.getWebStorageSizeManager().onReachedMaxAppCacheSize(
-                    spaceNeeded, totalUsedQuota, quotaUpdater);
+    void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
+        // if a view already exists then immediately terminate the new one
+        if (mCustomView != null) {
+            callback.onCustomViewHidden();
+            return;
         }
 
-        /**
-         * Instructs the browser to show a prompt to ask the user to set the
-         * Geolocation permission state for the specified origin.
-         * @param origin The origin for which Geolocation permissions are
-         *     requested.
-         * @param callback The callback to call once the user has set the
-         *     Geolocation permission state.
-         */
-        @Override
-        public void onGeolocationPermissionsShowPrompt(String origin,
-                GeolocationPermissions.Callback callback) {
-            mTabControl.getCurrentTab().getGeolocationPermissionsPrompt().show(
-                    origin, callback);
-        }
+        // Add the custom view to its container.
+        mCustomViewContainer.addView(view, COVER_SCREEN_GRAVITY_CENTER);
+        mCustomView = view;
+        mCustomViewCallback = callback;
+        // Save the menu state and set it to empty while the custom
+        // view is showing.
+        mOldMenuState = mMenuState;
+        mMenuState = EMPTY_MENU;
+        // Hide the content view.
+        mContentView.setVisibility(View.GONE);
+        // Finally show the custom view container.
+        setStatusBarVisibility(false);
+        mCustomViewContainer.setVisibility(View.VISIBLE);
+        mCustomViewContainer.bringToFront();
+    }
+
+    void onHideCustomView() {
+        if (mCustomView == null)
+            return;
 
-        /**
-         * Instructs the browser to hide the Geolocation permissions prompt.
-         */
-        @Override
-        public void onGeolocationPermissionsHidePrompt() {
-            mTabControl.getCurrentTab().getGeolocationPermissionsPrompt().hide();
-        }
+        // Hide the custom view.
+        mCustomView.setVisibility(View.GONE);
+        // Remove the custom view from its container.
+        mCustomViewContainer.removeView(mCustomView);
+        mCustomView = null;
+        // Reset the old menu state.
+        mMenuState = mOldMenuState;
+        mOldMenuState = EMPTY_MENU;
+        mCustomViewContainer.setVisibility(View.GONE);
+        mCustomViewCallback.onCustomViewHidden();
+        // Show the content view.
+        setStatusBarVisibility(true);
+        mContentView.setVisibility(View.VISIBLE);
+    }
 
-        /* Adds a JavaScript error message to the system log and if the JS
-         * console is enabled in the about:debug options, to that console
-         * also.
-         * @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 onConsoleMessage(String message, int lineNumber, String sourceID) {
-            ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true);
-            errorConsole.addErrorMessage(message, sourceID, lineNumber);
-                if (mShouldShowErrorConsole &&
-                        errorConsole.getShowState() != ErrorConsoleView.SHOW_MAXIMIZED) {
-                    errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED);
-                }
-            Log.w(LOGTAG, "Console: " + message + " " + sourceID + ":" + lineNumber);
+    Bitmap getDefaultVideoPoster() {
+        if (mDefaultVideoPoster == null) {
+            mDefaultVideoPoster = BitmapFactory.decodeResource(
+                    getResources(), R.drawable.default_video_poster);
         }
+        return mDefaultVideoPoster;
+    }
 
-        /**
-         * Ask the browser for an icon to represent a <video> element.
-         * This icon will be used if the Web page did not specify a poster attribute.
-         *
-         * @return Bitmap The icon or null if no such icon is available.
-         * @hide pending API Council approval
-         */
-        @Override
-        public Bitmap getDefaultVideoPoster() {
-            if (mDefaultVideoPoster == null) {
-                mDefaultVideoPoster = BitmapFactory.decodeResource(
-                        getResources(), R.drawable.default_video_poster);
-            }
-            return mDefaultVideoPoster;
-        }
-
-        /**
-         * Ask the host application for a custom progress view to show while
-         * a <video> is loading.
-         *
-         * @return View The progress view.
-         * @hide pending API Council approval
-         */
-        @Override
-        public View getVideoLoadingProgressView() {
-            if (mVideoProgressView == null) {
-                LayoutInflater inflater = LayoutInflater.from(BrowserActivity.this);
-                mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null);
-            }
-            return mVideoProgressView;
+    View getVideoLoadingProgressView() {
+        if (mVideoProgressView == null) {
+            LayoutInflater inflater = LayoutInflater.from(BrowserActivity.this);
+            mVideoProgressView = inflater.inflate(
+                    R.layout.video_loading_progress, null);
         }
+        return mVideoProgressView;
+    }
 
-        /**
-         * Deliver a list of already-visited URLs
-         * @hide pending API Council approval
-         */
-        @Override
-        public void getVisitedHistory(final ValueCallback<String[]> callback) {
-            AsyncTask<Void, Void, String[]> task = new AsyncTask<Void, Void, String[]>() {
-                public String[] doInBackground(Void... unused) {
-                    return Browser.getVisitedHistory(getContentResolver());
-                }
+    /*
+     * The Object used to inform the WebView of the file to upload.
+     */
+    private ValueCallback<Uri> mUploadMessage;
 
-                public void onPostExecute(String[] result) {
-                    callback.onReceiveValue(result);
+    void openFileChooser(ValueCallback<Uri> uploadMsg) {
+        if (mUploadMessage != null) return;
+        mUploadMessage = uploadMsg;
+        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
+        i.addCategory(Intent.CATEGORY_OPENABLE);
+        i.setType("*/*");
+        BrowserActivity.this.startActivityForResult(Intent.createChooser(i,
+                getString(R.string.choose_upload)), FILE_SELECTED);
+    }
 
-                };
-            };
-            task.execute();
-        };
-    };
+    // -------------------------------------------------------------------------
+    // Implement functions for DownloadListener
+    // -------------------------------------------------------------------------
 
     /**
      * Notify the host application a download should be done, or that
@@ -3568,6 +2791,36 @@ public class BrowserActivity extends Activity
         onDownloadStartNoStream(url, userAgent, contentDisposition, mimetype, contentLength);
     }
 
+    // This is to work around the fact that java.net.URI throws Exceptions
+    // instead of just encoding URL's properly
+    // Helper method for onDownloadStartNoStream
+    private static String encodePath(String path) {
+        char[] chars = path.toCharArray();
+
+        boolean needed = false;
+        for (char c : chars) {
+            if (c == '[' || c == ']') {
+                needed = true;
+                break;
+            }
+        }
+        if (needed == false) {
+            return path;
+        }
+
+        StringBuilder sb = new StringBuilder("");
+        for (char c : chars) {
+            if (c == '[' || c == ']') {
+                sb.append('%');
+                sb.append(Integer.toHexString(c));
+            } else {
+                sb.append(c);
+            }
+        }
+
+        return sb.toString();
+    }
+
     /**
      * Notify the host application a download should be done, even if there
      * is a streaming viewer available for thise type.
@@ -3607,35 +2860,16 @@ public class BrowserActivity extends Activity
             return;
         }
 
-        // java.net.URI is a lot stricter than KURL so we have to undo
-        // KURL's percent-encoding and redo the encoding using java.net.URI.
-        URI uri = null;
+        // java.net.URI is a lot stricter than KURL so we have to encode some
+        // extra characters. Fix for b 2538060 and b 1634719
+        WebAddress webAddress;
         try {
-            // Undo the percent-encoding that KURL may have done.
-            String newUrl = new String(URLUtil.decode(url.getBytes()));
-            // Parse the url into pieces
-            WebAddress w = new WebAddress(newUrl);
-            String frag = null;
-            String query = null;
-            String path = w.mPath;
-            // Break the path into path, query, and fragment
-            if (path.length() > 0) {
-                // Strip the fragment
-                int idx = path.lastIndexOf('#');
-                if (idx != -1) {
-                    frag = path.substring(idx + 1);
-                    path = path.substring(0, idx);
-                }
-                idx = path.lastIndexOf('?');
-                if (idx != -1) {
-                    query = path.substring(idx + 1);
-                    path = path.substring(0, idx);
-                }
-            }
-            uri = new URI(w.mScheme, w.mAuthInfo, w.mHost, w.mPort, path,
-                    query, frag);
+            webAddress = new WebAddress(url);
+            webAddress.mPath = encodePath(webAddress.mPath);
         } catch (Exception e) {
-            Log.e(LOGTAG, "Could not parse url for download: " + url, e);
+            // This only happens for very bad urls, we want to chatch the
+            // exception here
+            Log.e(LOGTAG, "Exception trying to parse url:" + url);
             return;
         }
 
@@ -3644,19 +2878,20 @@ public class BrowserActivity extends Activity
         String cookies = CookieManager.getInstance().getCookie(url);
 
         ContentValues values = new ContentValues();
-        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,
+        values.put(Downloads.Impl.COLUMN_URI, webAddress.toString());
+        values.put(Downloads.Impl.COLUMN_COOKIE_DATA, cookies);
+        values.put(Downloads.Impl.COLUMN_USER_AGENT, userAgent);
+        values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
                 getPackageName());
-        values.put(Downloads.COLUMN_NOTIFICATION_CLASS,
+        values.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
                 BrowserDownloadPage.class.getCanonicalName());
-        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());
+        values.put(Downloads.Impl.COLUMN_VISIBILITY,
+                Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+        values.put(Downloads.Impl.COLUMN_MIME_TYPE, mimetype);
+        values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, filename);
+        values.put(Downloads.Impl.COLUMN_DESCRIPTION, webAddress.mHost);
         if (contentLength > 0) {
-            values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength);
+            values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, contentLength);
         }
         if (mimetype == null) {
             // We must have long pressed on a link or image to download it. We
@@ -3664,53 +2899,29 @@ public class BrowserActivity extends Activity
             new FetchUrlMimeType(this).execute(values);
         } else {
             final Uri contentUri =
-                    getContentResolver().insert(Downloads.CONTENT_URI, values);
-            viewDownloads(contentUri);
+                    getContentResolver().insert(Downloads.Impl.CONTENT_URI, values);
         }
-
+        Toast.makeText(this, R.string.download_pending, Toast.LENGTH_SHORT)
+                .show();
     }
 
+    // -------------------------------------------------------------------------
+
     /**
      * Resets the lock icon. This method is called when we start a new load and
      * know the url to be loaded.
      */
     private void resetLockIcon(String url) {
         // Save the lock-icon state (we revert to it if the load gets cancelled)
-        saveLockIcon();
-
-        mLockIconType = LOCK_ICON_UNSECURE;
-        if (URLUtil.isHttpsUrl(url)) {
-            mLockIconType = LOCK_ICON_SECURE;
-            if (LOGV_ENABLED) {
-                Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
-                      " reset lock icon to " + mLockIconType);
-            }
-        }
-
+        mTabControl.getCurrentTab().resetLockIcon(url);
         updateLockIconImage(LOCK_ICON_UNSECURE);
     }
 
-    /* package */ void setLockIconType(int type) {
-        mLockIconType = type;
-    }
-
-    /* package */ int getLockIconType() {
-        return mLockIconType;
-    }
-
-    /* package */ void setPrevLockType(int type) {
-        mPrevLockType = type;
-    }
-
-    /* package */ int getPrevLockType() {
-        return mPrevLockType;
-    }
-
     /**
      * Update the lock icon to correspond to our latest state.
      */
-    /* package */ void updateLockIconToLatest() {
-        updateLockIconImage(mLockIconType);
+    private void updateLockIconToLatest() {
+        updateLockIconImage(mTabControl.getCurrentTab().getLockIconType());
     }
 
     /**
@@ -3724,9 +2935,7 @@ public class BrowserActivity extends Activity
             d = mMixLockIcon;
         }
         mTitleBar.setLock(d);
-        if (mFakeTitleBar != null) {
-            mFakeTitleBar.setLock(d);
-        }
+        mFakeTitleBar.setLock(d);
     }
 
     /**
@@ -3737,7 +2946,7 @@ public class BrowserActivity extends Activity
      * not. This is important, since we need to know whether to return to
      * the parent dialog or simply dismiss.
      */
-    private void showPageInfo(final TabControl.Tab tab,
+    private void showPageInfo(final Tab tab,
                               final boolean fromShowSSLCertificateOnError) {
         final LayoutInflater factory = LayoutInflater
                 .from(this);
@@ -3772,7 +2981,7 @@ public class BrowserActivity extends Activity
         ((TextView) pageInfoView.findViewById(R.id.title)).setText(title);
 
         mPageInfoView = tab;
-        mPageInfoFromShowSSLCertificateOnError = new Boolean(fromShowSSLCertificateOnError);
+        mPageInfoFromShowSSLCertificateOnError = fromShowSSLCertificateOnError;
 
         AlertDialog.Builder alertDialogBuilder =
             new AlertDialog.Builder(this)
@@ -3785,7 +2994,6 @@ public class BrowserActivity extends Activity
                                         int whichButton) {
                         mPageInfoDialog = null;
                         mPageInfoView = null;
-                        mPageInfoFromShowSSLCertificateOnError = null;
 
                         // if we came here from the SSL error dialog
                         if (fromShowSSLCertificateOnError) {
@@ -3802,7 +3010,6 @@ public class BrowserActivity extends Activity
                     public void onCancel(DialogInterface dialog) {
                         mPageInfoDialog = null;
                         mPageInfoView = null;
-                        mPageInfoFromShowSSLCertificateOnError = null;
 
                         // if we came here from the SSL error dialog
                         if (fromShowSSLCertificateOnError) {
@@ -3827,7 +3034,6 @@ public class BrowserActivity extends Activity
                                         int whichButton) {
                         mPageInfoDialog = null;
                         mPageInfoView = null;
-                        mPageInfoFromShowSSLCertificateOnError = null;
 
                         // if we came here from the SSL error dialog
                         if (fromShowSSLCertificateOnError) {
@@ -3855,7 +3061,7 @@ public class BrowserActivity extends Activity
      * (accessible from the Page-Info dialog).
      * @param tab The tab to show certificate for.
      */
-    private void showSSLCertificate(final TabControl.Tab tab) {
+    private void showSSLCertificate(final Tab tab) {
         final View certificateView =
                 inflateCertificateView(tab.getWebView().getCertificate());
         if (certificateView == null) {
@@ -3907,7 +3113,7 @@ public class BrowserActivity extends Activity
      * connection that resulted in an SSL error or proceeding per user request.
      * @param error The SSL error object.
      */
-    private void showSSLCertificateOnError(
+    void showSSLCertificateOnError(
         final WebView view, final SslErrorHandler handler, final SslError error) {
 
         final View certificateView =
@@ -3966,8 +3172,8 @@ public class BrowserActivity extends Activity
                                 mSSLCertificateOnErrorHandler = null;
                                 mSSLCertificateOnErrorError = null;
 
-                                mWebViewClient.onReceivedSslError(
-                                    view, handler, error);
+                                view.getWebViewClient().onReceivedSslError(
+                                                view, handler, error);
                             }
                         })
                  .setNeutralButton(R.string.page_info_view,
@@ -3992,8 +3198,8 @@ public class BrowserActivity extends Activity
                                 mSSLCertificateOnErrorHandler = null;
                                 mSSLCertificateOnErrorError = null;
 
-                                mWebViewClient.onReceivedSslError(
-                                    view, handler, error);
+                                view.getWebViewClient().onReceivedSslError(
+                                                view, handler, error);
                             }
                         })
                 .show();
@@ -4039,14 +3245,14 @@ public class BrowserActivity extends Activity
         }
 
         // issued on:
-        String issuedOn = reformatCertificateDate(
-            certificate.getValidNotBefore());
+        String issuedOn = formatCertificateDate(
+            certificate.getValidNotBeforeDate());
         ((TextView) certificateView.findViewById(R.id.issued_on))
             .setText(issuedOn);
 
         // expires on:
-        String expiresOn = reformatCertificateDate(
-            certificate.getValidNotAfter());
+        String expiresOn = formatCertificateDate(
+            certificate.getValidNotAfterDate());
         ((TextView) certificateView.findViewById(R.id.expires_on))
             .setText(expiresOn);
 
@@ -4054,37 +3260,25 @@ public class BrowserActivity extends Activity
     }
 
     /**
-     * Re-formats the certificate date (Date.toString()) string to
-     * a properly localized date string.
+     * Formats the certificate date to a properly localized date string.
      * @return Properly localized version of the certificate date string and
-     * the original certificate date string if fails to localize.
-     * If the original string is null, returns an empty string "".
+     * the "" if it fails to localize.
      */
-    private String reformatCertificateDate(String certificateDate) {
-      String reformattedDate = null;
-
-      if (certificateDate != null) {
-          Date date = null;
-          try {
-              date = java.text.DateFormat.getInstance().parse(certificateDate);
-          } catch (ParseException e) {
-              date = null;
-          }
-
-          if (date != null) {
-              reformattedDate =
-                  DateFormat.getDateFormat(this).format(date);
-          }
+    private String formatCertificateDate(Date certificateDate) {
+      if (certificateDate == null) {
+          return "";
       }
-
-      return reformattedDate != null ? reformattedDate :
-          (certificateDate != null ? certificateDate : "");
+      String formattedDate = DateFormat.getDateFormat(this).format(certificateDate);
+      if (formattedDate == null) {
+          return "";
+      }
+      return formattedDate;
     }
 
     /**
      * Displays an http-authentication dialog.
      */
-    private void showHttpAuthentication(final HttpAuthHandler handler,
+    void showHttpAuthentication(final HttpAuthHandler handler,
             final String host, final String realm, final String title,
             final String name, final String password, int focusId) {
         LayoutInflater factory = LayoutInflater.from(this);
@@ -4174,7 +3368,7 @@ public class BrowserActivity extends Activity
     public void setHttpAuthUsernamePassword(String host, String realm,
                                             String username,
                                             String password) {
-        WebView w = mTabControl.getCurrentWebView();
+        WebView w = getTopWindow();
         if (w != null) {
             w.setHttpAuthUsernamePassword(host, realm, username, password);
         }
@@ -4205,6 +3399,10 @@ public class BrowserActivity extends Activity
         }
     }
 
+    boolean isNetworkUp() {
+        return mIsNetworkUp;
+    }
+
     // This method shows the network dialog alerting the user that the net is
     // down. It will only show the dialog if mAlertDialog is null.
     private void createAndShowNetworkDialog() {
@@ -4220,6 +3418,8 @@ public class BrowserActivity extends Activity
     @Override
     protected void onActivityResult(int requestCode, int resultCode,
                                     Intent intent) {
+        if (getTopWindow() == null) return;
+
         switch (requestCode) {
             case COMBO_PAGE:
                 if (resultCode == RESULT_OK && intent != null) {
@@ -4228,14 +3428,31 @@ public class BrowserActivity extends Activity
                     if (extras != null && extras.getBoolean("new_window", false)) {
                         openTab(data);
                     } else {
-                        final TabControl.Tab currentTab =
+                        final Tab currentTab =
                                 mTabControl.getCurrentTab();
                         dismissSubWindow(currentTab);
                         if (data != null && data.length() != 0) {
-                            getTopWindow().loadUrl(data);
+                            loadUrl(getTopWindow(), data);
                         }
                     }
                 }
+                // Deliberately fall through to PREFERENCES_PAGE, since the
+                // same extra may be attached to the COMBO_PAGE
+            case PREFERENCES_PAGE:
+                if (resultCode == RESULT_OK && intent != null) {
+                    String action = intent.getStringExtra(Intent.EXTRA_TEXT);
+                    if (BrowserSettings.PREF_CLEAR_HISTORY.equals(action)) {
+                        mTabControl.removeParentChildRelationShips();
+                    }
+                }
+                break;
+            // Choose a file from the file picker.
+            case FILE_SELECTED:
+                if (null == mUploadMessage) break;
+                Uri result = intent == null || resultCode != RESULT_OK ? null
+                        : intent.getData();
+                mUploadMessage.onReceiveValue(result);
+                mUploadMessage = null;
                 break;
             default:
                 break;
@@ -4245,14 +3462,14 @@ public class BrowserActivity extends Activity
 
     /*
      * This method is called as a result of the user selecting the options
-     * menu to see the download window, or when a download changes state. It
-     * shows the download window ontop of the current window.
+     * menu to see the download window. It shows the download window on top of
+     * the current window.
      */
-    /* package */ void viewDownloads(Uri downloadRecord) {
+    private void viewDownloads(Uri downloadRecord) {
         Intent intent = new Intent(this,
                 BrowserDownloadPage.class);
         intent.setData(downloadRecord);
-        startActivityForResult(intent, this.DOWNLOAD_PAGE);
+        startActivityForResult(intent, BrowserActivity.DOWNLOAD_PAGE);
 
     }
 
@@ -4289,8 +3506,7 @@ public class BrowserActivity extends Activity
         intent.putExtra("url", url);
         intent.putExtra("thumbnail", thumbnail);
         // Disable opening in a new window if we have maxed out the windows
-        intent.putExtra("disable_new_window", mTabControl.getTabCount()
-                >= TabControl.MAX_TABS);
+        intent.putExtra("disable_new_window", !mTabControl.canCreateNewTab());
         intent.putExtra("touch_icon_url", current.getTouchIconUrl());
         if (startWithHistory) {
             intent.putExtra(CombinedBookmarkHistoryActivity.STARTING_TAB,
@@ -4300,16 +3516,56 @@ public class BrowserActivity extends Activity
     }
 
     // Called when loading from context menu or LOAD_URL message
-    private void loadURL(WebView view, String url) {
+    private void loadUrlFromContext(WebView view, String url) {
         // In case the user enters nothing.
         if (url != null && url.length() != 0 && view != null) {
             url = smartUrlFilter(url);
-            if (!mWebViewClient.shouldOverrideUrlLoading(view, url)) {
-                view.loadUrl(url);
+            if (!view.getWebViewClient().shouldOverrideUrlLoading(view, url)) {
+                loadUrl(view, url);
             }
         }
     }
 
+    /**
+     * Load the URL into the given WebView and update the title bar
+     * to reflect the new load.  Call this instead of WebView.loadUrl
+     * directly.
+     * @param view The WebView used to load url.
+     * @param url The URL to load.
+     */
+    private void loadUrl(WebView view, String url) {
+        updateTitleBarForNewLoad(view, url);
+        view.loadUrl(url);
+    }
+
+    /**
+     * Load UrlData into a Tab and update the title bar to reflect the new
+     * load.  Call this instead of UrlData.loadIn directly.
+     * @param t The Tab used to load.
+     * @param data The UrlData being loaded.
+     */
+    private void loadUrlDataIn(Tab t, UrlData data) {
+        updateTitleBarForNewLoad(t.getWebView(), data.mUrl);
+        data.loadIn(t);
+    }
+
+    /**
+     * If the WebView is the top window, update the title bar to reflect
+     * loading the new URL.  i.e. set its text, clear the favicon (which
+     * will be set once the page begins loading), and set the progress to
+     * INITIAL_PROGRESS to show that the page has begun to load. Called
+     * by loadUrl and loadUrlDataIn.
+     * @param view The WebView that is starting a load.
+     * @param url The URL that is being loaded.
+     */
+    private void updateTitleBarForNewLoad(WebView view, String url) {
+        if (view == getTopWindow()) {
+            setUrlTitle(url, null);
+            setFavicon(null);
+            onProgressChanged(view, INITIAL_PROGRESS);
+        }
+    }
+
     private String smartUrlFilter(Uri inUri) {
         if (inUri != null) {
             return smartUrlFilter(inUri.toString());
@@ -4317,16 +3573,6 @@ public class BrowserActivity extends Activity
         return null;
     }
 
-
-    // get window count
-
-    int getWindowCount(){
-      if(mTabControl != null){
-        return mTabControl.getTabCount();
-      }
-      return 0;
-    }
-
     protected static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
             "(?i)" + // switch on case insensitive matching
             "(" +    // begin group for schema
@@ -4383,7 +3629,7 @@ public class BrowserActivity extends Activity
                 }
             }
         } else {
-            if (Regex.WEB_URL_PATTERN.matcher(inUrl).matches()) {
+            if (Patterns.WEB_URL.matcher(inUrl).matches()) {
                 return URLUtil.guessUrl(inUrl);
             }
         }
@@ -4400,7 +3646,8 @@ public class BrowserActivity extends Activity
 
         mShouldShowErrorConsole = flag;
 
-        ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true);
+        ErrorConsoleView errorConsole = mTabControl.getCurrentTab()
+                .getErrorConsole(true);
 
         if (flag) {
             // Setting the show state of the console will cause it's the layout to be inflated.
@@ -4412,7 +3659,7 @@ public class BrowserActivity extends Activity
 
             // Now we can add it to the main view.
             mErrorConsoleContainer.addView(errorConsole,
-                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
+                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                                   ViewGroup.LayoutParams.WRAP_CONTENT));
         } else {
             mErrorConsoleContainer.removeView(errorConsole);
@@ -4420,13 +3667,75 @@ public class BrowserActivity extends Activity
 
     }
 
+    boolean shouldShowErrorConsole() {
+        return mShouldShowErrorConsole;
+    }
+
+    private void setStatusBarVisibility(boolean visible) {
+        int flag = visible ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN;
+        getWindow().setFlags(flag, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+    }
+
+
+    private void sendNetworkType(String type, String subtype) {
+        WebView w = mTabControl.getCurrentWebView();
+        if (w != null) {
+            w.setNetworkType(type, subtype);
+        }
+    }
+
+    private void packageChanged(String packageName, boolean wasAdded) {
+        WebView w = mTabControl.getCurrentWebView();
+        if (w == null) {
+            return;
+        }
+
+        if (wasAdded) {
+            w.addPackageName(packageName);
+        } else {
+            w.removePackageName(packageName);
+        }
+    }
+
+    private void addPackageNames(Set<String> packageNames) {
+        WebView w = mTabControl.getCurrentWebView();
+        if (w == null) {
+            return;
+        }
+
+        w.addPackageNames(packageNames);
+    }
+
+    private void getInstalledPackages() {
+        AsyncTask<Void, Void, Set<String> > task =
+            new AsyncTask<Void, Void, Set<String> >() {
+            protected Set<String> doInBackground(Void... unused) {
+                Set<String> installedPackages = new HashSet<String>();
+                PackageManager pm = BrowserActivity.this.getPackageManager();
+                if (pm != null) {
+                    List<PackageInfo> packages = pm.getInstalledPackages(0);
+                    for (PackageInfo p : packages) {
+                        if (BrowserActivity.this.sGoogleApps.contains(p.packageName)) {
+                            installedPackages.add(p.packageName);
+                        }
+                    }
+                }
+
+                return installedPackages;
+            }
+
+            // Executes on the UI thread
+            protected void onPostExecute(Set<String> installedPackages) {
+                addPackageNames(installedPackages);
+            }
+        };
+        task.execute();
+    }
+
     final static int LOCK_ICON_UNSECURE = 0;
     final static int LOCK_ICON_SECURE   = 1;
     final static int LOCK_ICON_MIXED    = 2;
 
-    private int mLockIconType = LOCK_ICON_UNSECURE;
-    private int mPrevLockType = LOCK_ICON_UNSECURE;
-
     private BrowserSettings mSettings;
     private TabControl      mTabControl;
     private ContentResolver mResolver;
@@ -4452,8 +3761,7 @@ public class BrowserActivity extends Activity
     private boolean mIsNetworkUp;
     private boolean mDidStopLoad;
 
-    private boolean mPageStarted;
-    private boolean mActivityInPause = true;
+    /* package */ boolean mActivityInPause = true;
 
     private boolean mMenuIsDown;
 
@@ -4486,9 +3794,6 @@ public class BrowserActivity extends Activity
     /* hold a ref so we can auto-cancel if necessary */
     private AlertDialog mAlertDialog;
 
-    // Wait for credentials before loading google.com
-    private ProgressDialog mCredsDlg;
-
     // The up-to-date URL and title (these can be different from those stored
     // in WebView, since it takes some time for the information in WebView to
     // get updated)
@@ -4498,11 +3803,11 @@ public class BrowserActivity extends Activity
     // As PageInfo has different style for landscape / portrait, we have
     // to re-open it when configuration changed
     private AlertDialog mPageInfoDialog;
-    private TabControl.Tab mPageInfoView;
+    private Tab mPageInfoView;
     // If the Page-Info dialog is launched from the SSL-certificate-on-error
     // dialog, we should not just dismiss it, but should get back to the
     // SSL-certificate-on-error dialog. This flag is used to store this state
-    private Boolean mPageInfoFromShowSSLCertificateOnError;
+    private boolean mPageInfoFromShowSSLCertificateOnError;
 
     // as SSLCertificateOnError has different style for landscape / portrait,
     // we have to re-open it when configuration changed
@@ -4514,7 +3819,7 @@ public class BrowserActivity extends Activity
     // as SSLCertificate has different style for landscape / portrait, we
     // have to re-open it when configuration changed
     private AlertDialog mSSLCertificateDialog;
-    private TabControl.Tab mSSLCertificateView;
+    private Tab mSSLCertificateView;
 
     // as HttpAuthentication has different style for landscape / portrait, we
     // have to re-open it when configuration changed
@@ -4523,12 +3828,12 @@ public class BrowserActivity extends Activity
 
     /*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS =
                                             new FrameLayout.LayoutParams(
-                                            ViewGroup.LayoutParams.FILL_PARENT,
-                                            ViewGroup.LayoutParams.FILL_PARENT);
+                                            ViewGroup.LayoutParams.MATCH_PARENT,
+                                            ViewGroup.LayoutParams.MATCH_PARENT);
     /*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER =
                                             new FrameLayout.LayoutParams(
-                                            ViewGroup.LayoutParams.FILL_PARENT,
-                                            ViewGroup.LayoutParams.FILL_PARENT,
+                                            ViewGroup.LayoutParams.MATCH_PARENT,
+                                            ViewGroup.LayoutParams.MATCH_PARENT,
                                             Gravity.CENTER);
     // Google search
     final static String QuickSearch_G = "http://www.google.com/m?q=%s";
@@ -4579,72 +3884,69 @@ public class BrowserActivity extends Activity
 
     private BroadcastReceiver mPackageInstallationReceiver;
 
-    // AsyncTask for downloading touch icons
-    /* package */ DownloadTouchIcon mTouchIconLoader;
+    private SystemAllowGeolocationOrigins mSystemAllowGeolocationOrigins;
 
     // activity requestCode
     final static int COMBO_PAGE                 = 1;
     final static int DOWNLOAD_PAGE              = 2;
     final static int PREFERENCES_PAGE           = 3;
+    final static int FILE_SELECTED              = 4;
 
     // the default <video> poster
     private Bitmap mDefaultVideoPoster;
     // the video progress view
     private View mVideoProgressView;
 
+    // The Google packages we monitor for the navigator.isApplicationInstalled()
+    // API. Add as needed.
+    private static Set<String> sGoogleApps;
+    static {
+        sGoogleApps = new HashSet<String>();
+        sGoogleApps.add("com.google.android.youtube");
+    }
+
     /**
      * 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;
-        byte[] mPostData;
+    /* package */ static class UrlData {
+        final String mUrl;
+        final Map<String, String> mHeaders;
+        final Intent mVoiceIntent;
 
         UrlData(String url) {
             this.mUrl = url;
+            this.mHeaders = null;
+            this.mVoiceIntent = null;
         }
 
-        void setPostData(byte[] postData) {
-            mPostData = postData;
+        UrlData(String url, Map<String, String> headers, Intent intent) {
+            this.mUrl = url;
+            this.mHeaders = headers;
+            if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS
+                    .equals(intent.getAction())) {
+                this.mVoiceIntent = intent;
+            } else {
+                this.mVoiceIntent = null;
+            }
         }
 
         boolean isEmpty() {
-            return mUrl == null || mUrl.length() == 0;
+            return mVoiceIntent == null && (mUrl == null || mUrl.length() == 0);
         }
 
-        public void loadIn(WebView webView) {
-            if (mPostData != null) {
-                webView.postUrl(mUrl, mPostData);
+        /**
+         * Load this UrlData into the given Tab.  Use loadUrlDataIn to update
+         * the title bar as well.
+         */
+        public void loadIn(Tab t) {
+            if (mVoiceIntent != null) {
+                t.activateVoiceSearchMode(mVoiceIntent);
             } else {
-                webView.loadUrl(mUrl);
+                t.getWebView().loadUrl(mUrl, mHeaders);
             }
         }
     };
 
-    /**
-     * 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;
-        @Override
-        boolean isEmpty() {
-            return mInlined == null || mInlined.length() == 0 || super.isEmpty();
-        }
-
-        @Override
-        public void loadIn(WebView webView) {
-            webView.loadDataWithBaseURL(null, mInlined, mMimeType, mEncoding, mUrl);
-        }
-    }
-
     /* package */ static final UrlData EMPTY_URL_DATA = new UrlData(null);
 }