Merge "Small startup perf improvement"
diff --git a/res/drawable-hdpi/ic_querybuilder_normal.png b/res/drawable-hdpi/ic_querybuilder_normal.png
index de63799..195329d 100644
--- a/res/drawable-hdpi/ic_querybuilder_normal.png
+++ b/res/drawable-hdpi/ic_querybuilder_normal.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_querybuilder_pressed.png b/res/drawable-hdpi/ic_querybuilder_pressed.png
index fdfebd1..1ad9e05 100644
--- a/res/drawable-hdpi/ic_querybuilder_pressed.png
+++ b/res/drawable-hdpi/ic_querybuilder_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_search_category_history.png b/res/drawable-hdpi/ic_search_category_history.png
index 91fe1f3..94c89ae 100644
--- a/res/drawable-hdpi/ic_search_category_history.png
+++ b/res/drawable-hdpi/ic_search_category_history.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_search_category_suggest.png b/res/drawable-hdpi/ic_search_category_suggest.png
index 9a17a9d..9ee751c 100644
--- a/res/drawable-hdpi/ic_search_category_suggest.png
+++ b/res/drawable-hdpi/ic_search_category_suggest.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_querybuilder_normal.png b/res/drawable-mdpi/ic_querybuilder_normal.png
index 66b538f..0d2858b 100644
--- a/res/drawable-mdpi/ic_querybuilder_normal.png
+++ b/res/drawable-mdpi/ic_querybuilder_normal.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_querybuilder_pressed.png b/res/drawable-mdpi/ic_querybuilder_pressed.png
index 0366d1e..0d2858b 100644
--- a/res/drawable-mdpi/ic_querybuilder_pressed.png
+++ b/res/drawable-mdpi/ic_querybuilder_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_search_category_history.png b/res/drawable-mdpi/ic_search_category_history.png
index 633e9c0..0f63144 100644
--- a/res/drawable-mdpi/ic_search_category_history.png
+++ b/res/drawable-mdpi/ic_search_category_history.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_search_category_suggest.png b/res/drawable-mdpi/ic_search_category_suggest.png
index 1001573..b9c374b 100644
--- a/res/drawable-mdpi/ic_search_category_suggest.png
+++ b/res/drawable-mdpi/ic_search_category_suggest.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_querybuilder_normal.png b/res/drawable-xhdpi/ic_querybuilder_normal.png
index cc3ce59..1a4680c 100644
--- a/res/drawable-xhdpi/ic_querybuilder_normal.png
+++ b/res/drawable-xhdpi/ic_querybuilder_normal.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_querybuilder_pressed.png b/res/drawable-xhdpi/ic_querybuilder_pressed.png
index 0693fa3..af9e7a1 100644
--- a/res/drawable-xhdpi/ic_querybuilder_pressed.png
+++ b/res/drawable-xhdpi/ic_querybuilder_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_category_bookmark.png b/res/drawable-xhdpi/ic_search_category_bookmark.png
index 8b5ffa1..6046a95 100644
--- a/res/drawable-xhdpi/ic_search_category_bookmark.png
+++ b/res/drawable-xhdpi/ic_search_category_bookmark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_category_browser.png b/res/drawable-xhdpi/ic_search_category_browser.png
index 8b195c1..bb767b4 100644
--- a/res/drawable-xhdpi/ic_search_category_browser.png
+++ b/res/drawable-xhdpi/ic_search_category_browser.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_category_history.png b/res/drawable-xhdpi/ic_search_category_history.png
index 8a0e828..681e4b4 100644
--- a/res/drawable-xhdpi/ic_search_category_history.png
+++ b/res/drawable-xhdpi/ic_search_category_history.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_category_suggest.png b/res/drawable-xhdpi/ic_search_category_suggest.png
index 985a201..d663b1b 100644
--- a/res/drawable-xhdpi/ic_search_category_suggest.png
+++ b/res/drawable-xhdpi/ic_search_category_suggest.png
Binary files differ
diff --git a/res/values-sw600dp/styles.xml b/res/values-sw600dp/styles.xml
index 41e09ae..699f10a 100644
--- a/res/values-sw600dp/styles.xml
+++ b/res/values-sw600dp/styles.xml
@@ -24,4 +24,8 @@
     </style>
     <style name="Suggestions" parent="@android:style/Widget.Holo.AutoCompleteTextView">
     </style>
+    <style name="ActionBarStyle" parent="@android:style/Widget.Holo.ActionBar">
+        <item name="android:background">@drawable/bg_browsertabs</item>
+        <item name="android:displayOptions"></item>
+    </style>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 20e7297..c9cb170 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -40,7 +40,7 @@
         <item name="android:windowIsTranslucent">true</item>
     </style>
     <style name="ActionBarStyle" parent="@android:style/Widget.Holo.ActionBar">
-        <item name="android:background">@drawable/bg_browsertabs</item>
+        <item name="android:background">@drawable/bg_urlbar</item>
         <item name="android:displayOptions"></item>
     </style>
     <style name="Suggestions" parent="@android:style/Widget.Holo.AutoCompleteTextView">
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index 7d7fda1..17ba4d7 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -804,10 +804,14 @@
         }
     }
 
-    protected void showTitleBarForDuration() {
+    protected final void showTitleBarForDuration() {
+        showTitleBarForDuration(HIDE_TITLEBAR_DELAY);
+    }
+
+    protected final void showTitleBarForDuration(long duration) {
         showTitleBar();
         Message msg = Message.obtain(mHandler, MSG_HIDE_TITLEBAR);
-        mHandler.sendMessageDelayed(msg, HIDE_TITLEBAR_DELAY);
+        mHandler.sendMessageDelayed(msg, duration);
     }
 
     private Handler mHandler = new Handler() {
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index b86759a..23aeed5 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -62,7 +62,6 @@
             finish();
             return;
         }
-
         mController = new Controller(this, icicle == null);
         boolean xlarge = isTablet(this);
         if (xlarge) {
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index ff1ad0e..67c42dd 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -2168,6 +2168,7 @@
             }
         }
         Tab t = tabControl.getTab();
+        t.refreshIdAfterPreload();
         mTabControl.addPreloadedTab(t);
         addTab(t);
         setActiveTab(t);
diff --git a/src/com/android/browser/PreloadedTabControl.java b/src/com/android/browser/PreloadedTabControl.java
index ed066f2..4ffe6b4 100644
--- a/src/com/android/browser/PreloadedTabControl.java
+++ b/src/com/android/browser/PreloadedTabControl.java
@@ -21,6 +21,7 @@
 import android.webkit.SearchBox;
 
 import java.util.Map;
+import java.util.regex.Pattern;
 
 /**
  * Class to manage the controlling of preloaded tab.
@@ -74,6 +75,7 @@
         }
         maybeSetQuery(query, sb);
         if (LOGD_ENABLED) Log.d(LOGTAG, "Submitting query " + query);
+        final String currentUrl = mTab.getUrl();
         sb.onsubmit(new SearchBox.SearchBoxListener() {
             @Override
             public void onSubmitComplete(boolean called) {
@@ -83,7 +85,21 @@
                     if (LOGD_ENABLED) Log.d(LOGTAG, "Query not submitted; falling back");
                     loadUrl(fallbackUrl, fallbackHeaders);
                     // make sure that the failed, preloaded URL is cleared from the back stack
-                    mTab.clearBackStackWhenItemAdded(fallbackUrl);
+                    mTab.clearBackStackWhenItemAdded(Pattern.compile(
+                            "^" + Pattern.quote(fallbackUrl) + "$"));
+                } else {
+                    // ignore the next fragment change, to avoid leaving a blank page in the browser
+                    // after the query has been submitted.
+                    String currentWithoutFragment = Uri.parse(currentUrl)
+                            .buildUpon()
+                            .fragment(null)
+                            .toString();
+                    mTab.clearBackStackWhenItemAdded(
+                            Pattern.compile(
+                                    "^" +
+                                    Pattern.quote(currentWithoutFragment) +
+                                    "(\\#.*)?" +
+                                    "$"));
                 }
             }});
         return true;
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 69213cd..8c9dc02 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -84,6 +84,7 @@
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Vector;
+import java.util.regex.Pattern;
 import java.util.zip.GZIPOutputStream;
 
 /**
@@ -175,7 +176,7 @@
     /**
      * See {@link #clearBackStackWhenItemAdded(String)}.
      */
-    private String mClearHistoryMatchUrl;
+    private Pattern mClearHistoryUrlPattern;
 
     private static synchronized Bitmap getDefaultFavicon(Context context) {
         if (sDefaultFavicon == null) {
@@ -1391,17 +1392,20 @@
                 if (isInVoiceSearchMode()) {
                     item.setCustomData(mVoiceSearchData.mVoiceSearchIntent);
                 }
-                if (mClearHistoryMatchUrl != null) {
+                if (mClearHistoryUrlPattern != null) {
+                    boolean match =
+                        mClearHistoryUrlPattern.matcher(item.getOriginalUrl()).matches();
                     if (LOGD_ENABLED) {
-                        Log.d(LOGTAG, "onNewHistoryItem:\n\t" + item.getUrl() + "\n\t"
-                                + mClearHistoryMatchUrl);
+                        Log.d(LOGTAG, "onNewHistoryItem: match=" + match + "\n\t"
+                                + item.getUrl() + "\n\t"
+                                + mClearHistoryUrlPattern);
                     }
-                    if (TextUtils.equals(item.getOriginalUrl(), mClearHistoryMatchUrl)) {
+                    if (match) {
                         if (mMainView != null) {
                             mMainView.clearHistory();
                         }
                     }
-                    mClearHistoryMatchUrl = null;
+                    mClearHistoryUrlPattern = null;
                 }
             }
             @Override
@@ -1435,6 +1439,15 @@
         };
     }
 
+    /**
+     * This is used to get a new ID when the tab has been preloaded, before it is displayed and
+     * added to TabControl. Preloaded tabs can be created before restoreInstanceState, leading
+     * to overlapping IDs between the preloaded and restored tabs.
+     */
+    public void refreshIdAfterPreload() {
+        mId = TabControl.getNextId();
+    }
+
     public void updateShouldCaptureThumbnails() {
         if (mWebViewController.shouldCaptureThumbnails()) {
             synchronized (Tab.this) {
@@ -2097,8 +2110,8 @@
      * This is used to ensure that preloaded URLs that are not subsequently seen by the user do
      * not appear in the back stack.
      */
-    public void clearBackStackWhenItemAdded(String urlToMatch) {
-        mClearHistoryMatchUrl = urlToMatch;
+    public void clearBackStackWhenItemAdded(Pattern urlPattern) {
+        mClearHistoryUrlPattern = urlPattern;
     }
 
     protected void persistThumbnail() {
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index 932a811..0668b74 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -170,6 +170,12 @@
     }
 
     void addPreloadedTab(Tab tab) {
+        for (Tab current : mTabs) {
+            if (current != null && current.getId() == tab.getId()) {
+                throw new IllegalStateException("Tab with id " + tab.getId() + " already exists: "
+                        + current.toString());
+            }
+        }
         mTabs.add(tab);
         tab.setController(mController);
         mController.onSetWebView(tab, tab.getWebView());
diff --git a/src/com/android/browser/UrlBarAutoShowManager.java b/src/com/android/browser/UrlBarAutoShowManager.java
index 25192ca..0db31ff 100644
--- a/src/com/android/browser/UrlBarAutoShowManager.java
+++ b/src/com/android/browser/UrlBarAutoShowManager.java
@@ -31,6 +31,8 @@
         OnScrollChangedListener {
 
     private static float V_TRIGGER_ANGLE = .9f;
+    private static long SCROLL_TIMEOUT_DURATION = 150;
+    private static long IGNORE_INTERVAL = 250;
 
     private BrowserWebView mTarget;
     private BaseUi mUi;
@@ -39,10 +41,10 @@
 
     private float mStartTouchX;
     private float mStartTouchY;
-    private float mLastTouchX;
-    private float mLastTouchY;
     private boolean mIsTracking;
     private boolean mHasTriggered;
+    private long mLastScrollTime;
+    private long mTriggeredTime;
 
     public UrlBarAutoShowManager(BaseUi ui) {
         mUi = ui;
@@ -66,11 +68,15 @@
 
     @Override
     public void onScrollChanged(int l, int t, int oldl, int oldt) {
+        mLastScrollTime = System.currentTimeMillis();
         if (t != oldt) {
             if (t != 0) {
                 // If it is showing, extend it
                 if (mUi.isTitleBarShowing()) {
-                    mUi.showTitleBarForDuration();
+                    long remaining = mLastScrollTime - mTriggeredTime;
+                    remaining = Math.max(BaseUi.HIDE_TITLEBAR_DELAY - remaining,
+                            SCROLL_TIMEOUT_DURATION);
+                    mUi.showTitleBarForDuration(remaining);
                 }
             } else {
                 mUi.suggestHideTitleBar();
@@ -95,6 +101,11 @@
         switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
             if (!mIsTracking && event.getPointerCount() == 1) {
+                long sinceLastScroll =
+                        System.currentTimeMillis() - mLastScrollTime;
+                if (sinceLastScroll < IGNORE_INTERVAL) {
+                    break;
+                }
                 mStartTouchY = event.getY();
                 mStartTouchX = event.getX();
                 mIsTracking = true;
@@ -113,6 +124,7 @@
                     if (dy > mSlop && angle > V_TRIGGER_ANGLE
                             && !mUi.isTitleBarShowing()
                             && web.getVisibleTitleHeight() == 0) {
+                        mTriggeredTime = System.currentTimeMillis();
                         mUi.showTitleBar();
                     }
                 }
diff --git a/tests/assets/bindings_test.html b/tests/assets/bindings_test.html
index 71f3438..2d20ada 100755
--- a/tests/assets/bindings_test.html
+++ b/tests/assets/bindings_test.html
@@ -7,7 +7,7 @@
 }
 
 function testObjectTypes() {
-  return JNIBindingsTest.testObjectTypes("Foo", "", {"foo":"bar"}, {});
+  return JNIBindingsTest.testObjectTypes("Foo", "", null, {"foo":"bar"}, {});
 }
 
 function testArray() {
@@ -104,6 +104,12 @@
     return false;
   }
 
+  returned = JNIBindingsTest.returnNullString();
+  if (returned !== undefined) {
+    appendLog("returnNullString() failed: expected undefined, got " + returned);
+    return false;
+  }
+
   returned = JNIBindingsTest.returnObject();
   if (returned == null) {
     appendLog("returnObject() failed: expected non-null, got " + returned);
diff --git a/tests/src/com/android/browser/JNIBindingsTest.java b/tests/src/com/android/browser/JNIBindingsTest.java
index 94dc985..8c25a80 100644
--- a/tests/src/com/android/browser/JNIBindingsTest.java
+++ b/tests/src/com/android/browser/JNIBindingsTest.java
@@ -82,8 +82,8 @@
         return true;
     }
 
-    public boolean testObjectTypes(String stringParam, String emptyString, Object objectParam,
-            Object emptyObject) {
+    public boolean testObjectTypes(String stringParam, String emptyString, String nullString,
+            Object objectParam, Object emptyObject) {
         String expectedString = "Foo";
         String expectedEmptyString = "";
 
@@ -92,6 +92,7 @@
             assertNotNull(emptyString);
             assertEquals(expectedString, stringParam);
             assertEquals(expectedEmptyString, emptyString);
+            assertNull(nullString);
             assertNull(objectParam);
             assertNull(emptyObject);
         } catch (AssertionFailedError e) {
@@ -267,6 +268,7 @@
     public long returnLong() { return 1234L; }
     public short returnShort() { return 12345; }
     public String returnString() { return "Hello World!"; }
+    public String returnNullString() { return null; }
 
     public class TestObject {
         public int x = 123;