OSDN Git Service

When reverting the voice title bar, display the url.
[android-x86/packages-apps-Browser.git] / src / com / android / browser / BrowserActivity.java
index 7a43e14..2469408 100644 (file)
@@ -23,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;
@@ -30,13 +32,13 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.ServiceConnection;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.database.DatabaseUtils;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
@@ -55,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;
@@ -67,11 +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.util.AttributeSet;
 import android.util.Log;
+import android.util.Patterns;
 import android.view.ContextMenu;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -102,11 +104,15 @@ 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.Patterns;
-
-import com.google.android.googleapps.IGoogleLoginService;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
+import com.android.common.Search;
+import com.android.common.speech.LoggingEvents;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -120,12 +126,17 @@ import java.net.URLEncoder;
 import java.text.ParseException;
 import java.util.Date;
 import java.util.HashMap;
+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;
 
 public class BrowserActivity extends Activity
-    implements View.OnCreateContextMenuListener,
-        DownloadListener {
+    implements View.OnCreateContextMenuListener, DownloadListener,
+        AccountManagerCallback<Account[]> {
 
     /* 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".
@@ -134,9 +145,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;
-
     // These are single-character shortcuts for searching popular sources.
     private static final int SHORTCUT_INVALID = 0;
     private static final int SHORTCUT_GOOGLE_SEARCH = 1;
@@ -144,89 +152,79 @@ public class BrowserActivity extends Activity
     private static final int SHORTCUT_DICTIONARY_SEARCH = 3;
     private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4;
 
-    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();
+    private Account[] mAccountsGoogle;
+    private Account[] mAccountsPreferHosted;
 
-                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);
-                }
-            }
+    // XXX: These constants should be exposed through some public api. Hardcode
+    // the values for now until some solution for gsf can be worked out.
+    // http://b/issue?id=2425179
+    private static final String ACCOUNT_TYPE = "com.google";
+    private static final String FEATURE_LEGACY_GOOGLE = "legacy_google";
+    private static final String FEATURE_LEGACY_HOSTED_OR_GOOGLE =
+            "legacy_hosted_or_google";
 
-            private void finish(final String homepage) {
-                mHandler.post(new Runnable() {
-                    public void run() {
-                        mSettings.setHomePage(BrowserActivity.this, homepage);
-                        resumeAfterCredentials();
+    private void startReadOfGoogleAccounts() {
+        mAccountsGoogle = null;
+        mAccountsPreferHosted = null;
 
-                        // 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();
-                }
+        AccountManager.get(this).getAccountsByTypeAndFeatures(
+                ACCOUNT_TYPE, new String[]{ FEATURE_LEGACY_HOSTED_OR_GOOGLE },
+                this, null);
+    }
+
+    /** This implements AccountManagerCallback<Account[]> */
+    public void run(AccountManagerFuture<Account[]> accountManagerFuture) {
+        try {
+            if (mAccountsGoogle == null) {
+                mAccountsGoogle = accountManagerFuture.getResult();
+
+                AccountManager.get(this).getAccountsByTypeAndFeatures(
+                        ACCOUNT_TYPE, new String[]{ FEATURE_LEGACY_GOOGLE },
+                        this, null);
+            } else {
+                mAccountsPreferHosted = accountManagerFuture.getResult();
+                setupHomePage();
             }
-            public void onServiceDisconnected(ComponentName className) {
-                mGls = null;
+        } catch (OperationCanceledException e) {
+            setupHomePage();
+        } catch (IOException e) {
+            setupHomePage();
+        } catch (AuthenticatorException e) {
+            setupHomePage();
+        }
+    }
+
+    private void setupHomePage() {
+        // get the default home page
+        String homepage = mSettings.getHomePage();
+
+        if (mAccountsPreferHosted != null && mAccountsGoogle != null) {
+            // 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)
+            String hostedUser = mAccountsPreferHosted.length == 0 
+                    ? null
+                    : mAccountsPreferHosted[0].name;
+            String googleUser = mAccountsGoogle.length == 0 ? null : mAccountsGoogle[0].name;
+
+            // 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 + "?");
             }
-        };
+        }
 
-        bindService(GoogleLoginServiceConstants.SERVICE_INTENT,
-                    mGlsConnection, Context.BIND_AUTO_CREATE);
+        mSettings.setHomePage(BrowserActivity.this, homepage);
+        resumeAfterCredentials();
     }
 
     private static class ClearThumbnails extends AsyncTask<File, Void, Void> {
@@ -258,6 +256,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();
@@ -302,6 +306,14 @@ public class BrowserActivity extends Activity
         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();
@@ -312,9 +324,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());
                     }
                 }
             };
@@ -334,6 +352,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 {
@@ -381,9 +405,12 @@ public class BrowserActivity extends Activity
             // the tab will be close when exit.
             UrlData urlData = getUrlDataFromIntent(intent);
 
+            String action = intent.getAction();
             final Tab t = mTabControl.createNewTab(
-                    Intent.ACTION_VIEW.equals(intent.getAction()) &&
-                    intent.getData() != null,
+                    (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);
@@ -400,21 +427,17 @@ public class BrowserActivity extends Activity
             // asset directory to the data partition.
             if ((extra == null || !extra.getBoolean("testing"))
                     && !mSettings.isLoginInitialized()) {
-                setupHomePage();
+                startReadOfGoogleAccounts();
             }
 
             if (urlData.isEmpty()) {
                 if (mSettings.isLoginInitialized()) {
-                    webView.loadUrl(mSettings.getHomePage());
+                    loadUrl(webView, mSettings.getHomePage());
                 } else {
                     waitForCredentials();
                 }
             } 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
@@ -427,6 +450,26 @@ public class BrowserActivity extends Activity
         if (jsFlags.trim().length() != 0) {
             mTabControl.getCurrentWebView().setJsFlags(jsFlags);
         }
+        // Work out which packages are installed on the system.
+        getInstalledPackages();
+    }
+
+    /**
+     * 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();
+
+        startSearch(result, false,
+                createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY),
+                false);
     }
 
     @Override
@@ -452,10 +495,36 @@ public class BrowserActivity extends Activity
             // just resume the browser
             return;
         }
+        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)) {
@@ -466,12 +535,10 @@ 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) || activateVoiceSearch)
                     && !getPackageName().equals(appId)
                     && (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
                 Tab appTab = mTabControl.getTabFromId(appId);
@@ -491,14 +558,14 @@ public class BrowserActivity extends Activity
                     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;
@@ -533,6 +600,13 @@ public class BrowserActivity extends Activity
                         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();
                     }
@@ -540,7 +614,7 @@ public class BrowserActivity extends Activity
                 }
                 // Get rid of the subwindow if it exists
                 dismissSubWindow(current);
-                urlData.loadIn(current.getWebView());
+                loadUrlDataIn(current, urlData);
             }
         }
     }
@@ -570,6 +644,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();
@@ -621,7 +699,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)) {
@@ -633,12 +712,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)
@@ -646,17 +730,17 @@ public class BrowserActivity extends Activity
                 url = intent.getStringExtra(SearchManager.QUERY);
                 if (url != null) {
                     mLastEnteredUrl = url;
-                    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);
+                    Browser.updateVisitedHistory(mResolver, url, false);
                     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;
@@ -666,9 +750,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().
@@ -750,7 +847,7 @@ public class BrowserActivity extends Activity
      */
     private FrameLayout.LayoutParams mFakeTitleBarParams
             = new FrameLayout.LayoutParams(
-            ViewGroup.LayoutParams.FILL_PARENT,
+            ViewGroup.LayoutParams.MATCH_PARENT,
             ViewGroup.LayoutParams.WRAP_CONTENT);
     /**
      * Keeps track of whether the options menu is open.  This is important in
@@ -856,7 +953,7 @@ 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.FLAG_NOT_FOCUSABLE,
@@ -1018,10 +1115,6 @@ public class BrowserActivity extends Activity
         // Destroy all the tabs
         mTabControl.destroy();
         WebIconDatabase.getInstance().close();
-        if (mGlsConnection != null) {
-            unbindService(mGlsConnection);
-            mGlsConnection = null;
-        }
 
         unregisterReceiver(mPackageInstallationReceiver);
     }
@@ -1035,7 +1128,7 @@ public class BrowserActivity extends Activity
             mPageInfoDialog.dismiss();
             showPageInfo(
                 mPageInfoView,
-                mPageInfoFromShowSSLCertificateOnError.booleanValue());
+                mPageInfoFromShowSSLCertificateOnError);
         }
         if (mSSLCertificateDialog != null) {
             mSSLCertificateDialog.dismiss();
@@ -1143,7 +1236,7 @@ public class BrowserActivity extends Activity
         // Load the page
         WebView w = mTabControl.getCurrentWebView();
         if (w != null) {
-            w.loadUrl(mSettings.getHomePage());
+            loadUrl(w, mSettings.getHomePage());
         }
 
         // Update the settings, need to do this last as it can take a moment
@@ -1216,19 +1309,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:
@@ -1239,7 +1334,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);
@@ -1250,30 +1346,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) {
@@ -1383,7 +1477,7 @@ public class BrowserActivity extends Activity
                 break;
 
             case R.id.goto_menu_id:
-                onSearchRequested();
+                editUrl();
                 break;
 
             case R.id.bookmarks_menu_id:
@@ -1439,7 +1533,7 @@ public class BrowserActivity extends Activity
                 Tab current = mTabControl.getCurrentTab();
                 if (current != null) {
                     dismissSubWindow(current);
-                    current.getWebView().loadUrl(mSettings.getHomePage());
+                    loadUrl(current.getWebView(), mSettings.getHomePage());
                 }
                 break;
 
@@ -1457,6 +1551,7 @@ public class BrowserActivity extends Activity
                 }
                 mFindDialog.setWebView(getTopWindow());
                 mFindDialog.show();
+                getTopWindow().setFindIsUp(true);
                 mMenuState = EMPTY_MENU;
                 break;
 
@@ -1471,15 +1566,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;
@@ -1584,6 +1691,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;
@@ -1593,6 +1706,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) {
@@ -1722,12 +1838,17 @@ 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));
         }
 
         WebView view = t.getWebView();
         view.setEmbeddedTitleBar(mTitleBar);
+        if (t.isInVoiceSearchMode()) {
+            showVoiceTitleBar(t.getVoiceDisplayTitle());
+        } else {
+            revertVoiceTitleBar();
+        }
         // Request focus on the top window.
         t.getTopWindow().requestFocus();
     }
@@ -1789,7 +1910,7 @@ public class BrowserActivity extends Activity
             mTabControl.setCurrentTab(tab);
             attachTabToContentView(tab);
             if (!urlData.isEmpty()) {
-                urlData.loadIn(webview);
+                loadUrlDataIn(tab, urlData);
             }
             return tab;
         } else {
@@ -1797,10 +1918,10 @@ 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 Tab openTab(String url) {
@@ -1808,7 +1929,7 @@ public class BrowserActivity extends Activity
             Tab t = mTabControl.createNewTab();
             if (t != null) {
                 WebView view = t.getWebView();
-                view.loadUrl(url);
+                loadUrl(view, url);
             }
             return t;
         } else {
@@ -1977,8 +2098,10 @@ public class BrowserActivity extends Activity
         mUrl = url;
         mTitle = title;
 
-        mTitleBar.setTitleAndUrl(title, url);
-        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);
     }
 
     /**
@@ -2039,7 +2162,7 @@ public class BrowserActivity extends Activity
         resetTitleIconAndProgress();
     }
 
-    private void goBackOnePageOrQuit() {
+    /* package */ void goBackOnePageOrQuit() {
         Tab current = mTabControl.getCurrentTab();
         if (current == null) {
             /*
@@ -2250,7 +2373,7 @@ 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 Tab parent = mTabControl.getCurrentTab();
@@ -2267,8 +2390,41 @@ public class BrowserActivity extends Activity
                             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);
@@ -2282,7 +2438,7 @@ public class BrowserActivity extends Activity
                 }
 
                 case LOAD_URL:
-                    loadURL(getTopWindow(), (String) msg.obj);
+                    loadUrlFromContext(getTopWindow(), (String) msg.obj);
                     break;
 
                 case STOP_LOAD:
@@ -2313,6 +2469,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?
@@ -2432,6 +2617,10 @@ 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;";
 
+    // 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
@@ -2442,11 +2631,9 @@ public class BrowserActivity extends Activity
         resetLockIcon(url);
         setUrlTitle(url, null);
         setFavicon(favicon);
-        // Keep this initial progress in sync with initialProgressValue (* 100)
-        // in ProgressTracker.cpp
         // Show some progress so that the user knows the page is beginning to
         // load
-        onProgressChanged(view, 10);
+        onProgressChanged(view, INITIAL_PROGRESS);
         mDidStopLoad = false;
         if (!mIsNetworkUp) createAndShowNetworkDialog();
 
@@ -2648,12 +2835,19 @@ public class BrowserActivity extends Activity
                     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;
-            updateInLoadMenuItems();
+        } 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();
+            }
+            // 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();
@@ -2865,19 +3059,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, uri.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, uri.getHost());
         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
@@ -2885,8 +3080,7 @@ 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);
         }
 
     }
@@ -2967,7 +3161,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)
@@ -2980,7 +3174,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) {
@@ -2997,7 +3190,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) {
@@ -3022,7 +3214,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) {
@@ -3234,14 +3425,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);
 
@@ -3249,31 +3440,19 @@ 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;
     }
 
     /**
@@ -3433,10 +3612,19 @@ public class BrowserActivity extends Activity
                                 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:
@@ -3454,10 +3642,10 @@ 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);
@@ -3508,16 +3696,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 (!view.getWebViewClient().shouldOverrideUrlLoading(view, url)) {
-                view.loadUrl(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());
@@ -3611,7 +3839,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);
@@ -3628,6 +3856,62 @@ public class BrowserActivity extends Activity
         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;
@@ -3706,7 +3990,7 @@ public class BrowserActivity extends Activity
     // 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
@@ -3727,12 +4011,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";
@@ -3794,59 +4078,56 @@ public class BrowserActivity extends Activity
     // 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;
+        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);
 }