Merge "Import revised translations."
diff --git a/res/drawable-hdpi/browsertab_add.png b/res/drawable-hdpi/browsertab_add.png
new file mode 100644
index 0000000..a48b0f8
--- /dev/null
+++ b/res/drawable-hdpi/browsertab_add.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_new_window.png b/res/drawable-hdpi/ic_menu_new_window.png
index fa544fc..fd85bb7 100644
--- a/res/drawable-hdpi/ic_menu_new_window.png
+++ b/res/drawable-hdpi/ic_menu_new_window.png
Binary files differ
diff --git a/res/drawable-mdpi/bg_browsertabs.png b/res/drawable-mdpi/bg_browsertabs.png
new file mode 100644
index 0000000..9d0ff07
--- /dev/null
+++ b/res/drawable-mdpi/bg_browsertabs.png
Binary files differ
diff --git a/res/drawable-mdpi/browsertab_add.png b/res/drawable-mdpi/browsertab_add.png
new file mode 100644
index 0000000..cb12775
--- /dev/null
+++ b/res/drawable-mdpi/browsertab_add.png
Binary files differ
diff --git a/res/drawable-mdpi/browsertab_inactive.png b/res/drawable-mdpi/browsertab_inactive.png
new file mode 100644
index 0000000..d441604
--- /dev/null
+++ b/res/drawable-mdpi/browsertab_inactive.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_new_window.png b/res/drawable-mdpi/ic_menu_new_window.png
index c767979..c495162 100644
--- a/res/drawable-mdpi/ic_menu_new_window.png
+++ b/res/drawable-mdpi/ic_menu_new_window.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_tab_close.png b/res/drawable-mdpi/ic_tab_close.png
index 2f23842..b86c714 100644
--- a/res/drawable-mdpi/ic_tab_close.png
+++ b/res/drawable-mdpi/ic_tab_close.png
Binary files differ
diff --git a/res/drawable-nodpi/bg_urlbar.png b/res/drawable-nodpi/bg_urlbar.png
new file mode 100644
index 0000000..ff173c4
--- /dev/null
+++ b/res/drawable-nodpi/bg_urlbar.png
Binary files differ
diff --git a/res/layout/tab_bar.xml b/res/layout/tab_bar.xml
index 2726055..e8f146c 100644
--- a/res/layout/tab_bar.xml
+++ b/res/layout/tab_bar.xml
@@ -14,20 +14,24 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/tabbarcontent"
     android:layout_width="match_parent"
-    android:layout_height="48dip"
+    android:layout_height="56dip"
     android:orientation="horizontal"
+    android:paddingLeft="12dip"
+    android:paddingTop="12dip"
+    android:paddingRight="0dip"
+    android:paddingBottom="0dip"
     style="@style/ActionBarStyle"
     >
     <com.android.browser.TabScrollView
         android:id="@+id/tabs"
         android:layout_width="wrap_content"
-        android:layout_height="match_parent"
+        android:layout_height="44dip"
         android:orientation="horizontal" />
     <ImageButton
         android:id="@+id/newtab"
         android:src="@drawable/ic_menu_new_window"
         android:layout_width="wrap_content"
-        android:layout_height="match_parent"
+        android:layout_height="44dip"
         style="@style/HoloButton"
-        android:background="@drawable/browserbarbutton" />
+        android:background="@drawable/browsertab_add" />
 </merge>
\ No newline at end of file
diff --git a/res/layout/tab_title.xml b/res/layout/tab_title.xml
index a2da03d..e4a4e58 100644
--- a/res/layout/tab_title.xml
+++ b/res/layout/tab_title.xml
@@ -15,8 +15,7 @@
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
     android:gravity="center_vertical"
-    android:orientation="horizontal"
-    android:background="@drawable/tab_background">
+    android:orientation="horizontal">
     <ImageView
         android:id="@+id/incognito"
         android:layout_width="16dip"
@@ -56,6 +55,5 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginLeft="16dip"
-        android:layout_marginRight="16dip"
         android:src="@drawable/ic_tab_close" />
 </merge>
diff --git a/res/layout/url_bar.xml b/res/layout/url_bar.xml
index f695ae3..3636ca8 100644
--- a/res/layout/url_bar.xml
+++ b/res/layout/url_bar.xml
@@ -21,7 +21,7 @@
         android:layout_width="match_parent"
         android:layout_height="48dip"
         android:orientation="horizontal"
-        android:background="@drawable/urlbar_bg">
+        android:background="@drawable/bg_urlbar">
         <ImageButton
             android:id="@+id/back"
             android:src="@drawable/ic_back_normal"
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
index 952d97a..d87a347 100644
--- a/res/values-xlarge/styles.xml
+++ b/res/values-xlarge/styles.xml
@@ -43,9 +43,8 @@
     <style name="ShortCutTheme" parent="@android:Theme.Holo">
     </style>
     <style name="ActionBarStyle">
-        <item name="android:height">48dip</item>
-        <item name="android:padding">0dip</item>
-        <item name="android:background">@drawable/tabbar_bg</item>
+        <item name="android:height">56dip</item>
+        <item name="android:background">@drawable/bg_browsertabs</item>
         <item name="android:displayOptions"></item>
     </style>
     <style name="ActionButton">
diff --git a/res/values/dimensions.xml b/res/values/dimensions.xml
index d51ab33..7cfa3f6 100644
--- a/res/values/dimensions.xml
+++ b/res/values/dimensions.xml
@@ -13,12 +13,14 @@
 <resources
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- The width of a selected tab -->
-    <dimen
-        name="tab_width_selected">300dp</dimen>
+    <dimen name="tab_width_selected">280dp</dimen>
     <!-- The width of an unselected tab -->
-    <dimen
-        name="tab_width_unselected">300dp</dimen>
-
+    <dimen name="tab_width_unselected">240dp</dimen>
+    <dimen name="tab_height">44dp</dimen>
+    <dimen name="tab_overlap">8dp</dimen>
+    <dimen name="tab_slice">18dp</dimen>
+    <dimen name="tab_padding">16dp</dimen>
+    <dimen name="max_tab_width">300dp</dimen>
     <dimen name="bookmarkThumbnailWidth">90dip</dimen>
     <dimen name="bookmarkThumbnailHeight">80dip</dimen>
     <dimen name="add_bookmark_width">500dip</dimen>
diff --git a/res/values/integers.xml b/res/values/integers.xml
index 2864d47..e18676d 100644
--- a/res/values/integers.xml
+++ b/res/values/integers.xml
@@ -19,7 +19,7 @@
     <!--  The maximum number of open tabs -->
     <integer name="max_tabs">16</integer>
     <!--  The duration of the tab animations in millisecs  -->
-    <integer name="tab_animation_duration">500</integer>
+    <integer name="tab_animation_duration">200</integer>
     <integer name="max_width_crumb">200</integer>
     <!-- The maximum number of most visited URLs in the history tab -->
     <integer name="most_visits_limit">10</integer>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c6adaef..1c8b71c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -879,4 +879,8 @@
     <!-- Menu item to share URL selection [CHAR LIMIT=30] -->
     <string name="menu_share_url">Share</string>
 
+    <!-- Toast to inform the user that the maximum number of tabs has been
+         reached. [CHAR LIMIT=50] -->
+    <string name="max_tabs_warning">No more tabs available</string>
+
 </resources>
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index b9ccd72..4fca79d 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -508,6 +508,13 @@
         mContentView.addView(container, COVER_SCREEN_PARAMS);
     }
 
+    int getTitleBarWidth() {
+        if (mTitleBar != null) {
+            return mTitleBar.getWidth();
+        }
+        return 0;
+    }
+
     void showFakeTitleBar() {
         if (!isFakeTitleBarShowing() && mActiveTabsPage == null &&
                 !mActivityPaused) {
@@ -932,4 +939,12 @@
         return mVideoProgressView;
     }
 
+    @Override
+    public void showMaxTabsWarning() {
+        Toast warning = Toast.makeText(mActivity,
+                mActivity.getString(R.string.max_tabs_warning),
+                Toast.LENGTH_SHORT);
+        warning.show();
+    }
+
 }
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index e4b0982..0227621 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -195,6 +195,7 @@
     // Checks to see when the bookmarks database has changed, and updates the
     // Tabs' notion of whether they represent bookmarked sites.
     private ContentObserver mBookmarksObserver;
+    private DataController mDataController;
 
     private static class ClearThumbnails extends AsyncTask<File, Void, Void> {
         @Override
@@ -213,6 +214,7 @@
     public Controller(Activity browser) {
         mActivity = browser;
         mSettings = BrowserSettings.getInstance();
+        mDataController = DataController.getInstance(mActivity);
         mTabControl = new TabControl(this);
         mSettings.setController(this);
 
@@ -843,42 +845,7 @@
         }
         // Update the title in the history database if not in private browsing mode
         if (!tab.isPrivateBrowsingEnabled()) {
-            new AsyncTask<Void, Void, Void>() {
-                @Override
-                protected Void doInBackground(Void... unused) {
-                    // See if we can find the current url in our history
-                    // database and add the new title to it.
-                    String url = pageUrl;
-                    if (url.startsWith("http://www.")) {
-                        url = url.substring(11);
-                    } else if (url.startsWith("http://")) {
-                        url = url.substring(4);
-                    }
-                    // Escape wildcards for LIKE operator.
-                    url = url.replace("\\", "\\\\").replace("%", "\\%")
-                            .replace("_", "\\_");
-                    Cursor c = null;
-                    try {
-                        final ContentResolver cr =
-                                getActivity().getContentResolver();
-                        String selection = History.URL + " LIKE ? ESCAPE '\\'";
-                        String [] selectionArgs = new String[] { "%" + url };
-                        ContentValues values = new ContentValues();
-                        values.put(History.TITLE, title);
-                        cr.update(History.CONTENT_URI, values, selection,
-                                selectionArgs);
-                    } catch (IllegalStateException e) {
-                        Log.e(LOGTAG, "Tab onReceived title", e);
-                    } catch (SQLiteException ex) {
-                        Log.e(LOGTAG,
-                                "onReceivedTitle() caught SQLiteException: ",
-                                ex);
-                    } finally {
-                        if (c != null) c.close();
-                    }
-                    return null;
-                }
-            }.execute();
+            mDataController.updateHistoryTitle(pageUrl, title);
         }
     }
 
@@ -924,28 +891,7 @@
         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);
-            }
-        }
-        final ContentResolver cr = getActivity().getContentResolver();
-        final String newUrl = url;
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... unused) {
-                Browser.updateVisitedHistory(cr, newUrl, true);
-                return null;
-            }
-        }.execute();
+        mDataController.updateVisitedHistory(url);
         WebIconDatabase.getInstance().retainIconForPageUrl(url);
     }
 
@@ -2107,9 +2053,19 @@
         }
     }
 
+    @Override
+    public Tab openTabToHomePage() {
+        // check for max tabs
+        if (mTabControl.canCreateNewTab()) {
+            return openTabAndShow(mSettings.getHomePage(), false, null);
+        } else {
+            mUi.showMaxTabsWarning();
+            return null;
+        }
+    }
+
     // A wrapper function of {@link #openTabAndShow(UrlData, boolean, String)}
     // that accepts url as string.
-
     protected Tab openTabAndShow(String url, boolean closeOnExit, String appId) {
         return openTabAndShow(new UrlData(url), closeOnExit, appId);
     }
@@ -2166,8 +2122,10 @@
             addTab(tab);
             setActiveTab(tab);
             return tab;
+        } else {
+            mUi.showMaxTabsWarning();
+            return null;
         }
-        return null;
     }
 
     /**
@@ -2191,11 +2149,6 @@
     }
 
     @Override
-    public Tab openTabToHomePage() {
-        return openTabAndShow(mSettings.getHomePage(), false, null);
-    }
-
-    @Override
     public void closeCurrentTab() {
         // hide combo view if open
         removeComboView();
diff --git a/src/com/android/browser/DataController.java b/src/com/android/browser/DataController.java
new file mode 100644
index 0000000..be38d70
--- /dev/null
+++ b/src/com/android/browser/DataController.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.browser;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.BrowserContract.History;
+
+public class DataController {
+    // Message IDs
+    private static final int HISTORY_UPDATE_VISITED = 100;
+    private static final int HISTORY_UPDATE_TITLE = 101;
+    private static DataController sInstance;
+
+    private Context mContext;
+    private Handler mHandler;
+
+    /* package */ static DataController getInstance(Context c) {
+        if (sInstance == null) {
+            sInstance = new DataController(c);
+        }
+        return sInstance;
+    }
+
+    private DataController(Context c) {
+        mContext = c.getApplicationContext();
+        HandlerThread thread = new HandlerThread("DataController");
+        thread.setDaemon(true);
+        thread.start();
+        mHandler = new DataControllerHandler(thread.getLooper());
+    }
+
+    public void updateVisitedHistory(String url) {
+        mHandler.obtainMessage(HISTORY_UPDATE_VISITED, url).sendToTarget();
+    }
+
+    public void updateHistoryTitle(String url, String title) {
+        mHandler.obtainMessage(HISTORY_UPDATE_TITLE, new String[] { url, title })
+                .sendToTarget();
+    }
+
+    class DataControllerHandler extends Handler {
+        public DataControllerHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case HISTORY_UPDATE_VISITED:
+                doUpdateVisitedHistory((String) msg.obj);
+                break;
+            case HISTORY_UPDATE_TITLE:
+                String[] args = (String[]) msg.obj;
+                doUpdateHistoryTitle(args[0], args[1]);
+                break;
+            }
+        }
+    }
+
+    private void doUpdateVisitedHistory(String url) {
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor c = null;
+        try {
+            c = cr.query(History.CONTENT_URI, new String[] { History._ID, History.VISITS },
+                    History.URL + "=?", new String[] { url }, null);
+            if (c.moveToFirst()) {
+                ContentValues values = new ContentValues();
+                values.put(History.VISITS, c.getInt(1) + 1);
+                values.put(History.DATE_LAST_VISITED, System.currentTimeMillis());
+                cr.update(ContentUris.withAppendedId(History.CONTENT_URI, c.getLong(0)),
+                        values, null, null);
+            } else {
+                android.provider.Browser.truncateHistory(cr);
+                ContentValues values = new ContentValues();
+                values.put(History.URL, url);
+                values.put(History.VISITS, 1);
+                values.put(History.DATE_LAST_VISITED, System.currentTimeMillis());
+                values.put(History.TITLE, url);
+                values.put(History.DATE_CREATED, 0);
+                values.put(History.USER_ENTERED, 0);
+                cr.insert(History.CONTENT_URI, values);
+            }
+        } finally {
+            if (c != null) c.close();
+        }
+    }
+
+    private void doUpdateHistoryTitle(String url, String title) {
+        ContentResolver cr = mContext.getContentResolver();
+        ContentValues values = new ContentValues();
+        values.put(History.TITLE, title);
+        cr.update(History.CONTENT_URI, values, History.URL + "=?",
+                new String[] { url });
+    }
+}
diff --git a/src/com/android/browser/IntentHandler.java b/src/com/android/browser/IntentHandler.java
index 040af81..0b47668 100644
--- a/src/com/android/browser/IntentHandler.java
+++ b/src/com/android/browser/IntentHandler.java
@@ -222,19 +222,6 @@
                     // But currently, we get the user-typed URL from search box as well.
                     url = UrlUtils.fixUrl(url);
                     url = UrlUtils.smartUrlFilter(url);
-                    final ContentResolver cr = mActivity.getContentResolver();
-                    final String newUrl = url;
-                    if (mTabControl == null
-                            || mTabControl.getCurrentWebView() == null
-                            || !mTabControl.getCurrentWebView().isPrivateBrowsingEnabled()) {
-                        new AsyncTask<Void, Void, Void>() {
-                            @Override
-                            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;
@@ -311,7 +298,6 @@
             new AsyncTask<Void, Void, Void>() {
                 @Override
                 protected Void doInBackground(Void... unused) {
-                        Browser.updateVisitedHistory(cr, newUrl, false);
                         Browser.addSearchUrl(cr, newUrl);
                     return null;
                 }
diff --git a/src/com/android/browser/ShortcutActivity.java b/src/com/android/browser/ShortcutActivity.java
index 33e192a..16a4cbe 100644
--- a/src/com/android/browser/ShortcutActivity.java
+++ b/src/com/android/browser/ShortcutActivity.java
@@ -40,6 +40,7 @@
         mBookmarks.setEnableContextMenu(false);
         mBookmarks.setBreadCrumbMaxVisible(2);
         mBookmarks.setBreadCrumbUseBackButton(true);
+        mBookmarks.setCallbackListener(this);
         View cancel = findViewById(R.id.cancel);
         if (cancel != null) {
             cancel.setOnClickListener(this);
diff --git a/src/com/android/browser/TabBar.java b/src/com/android/browser/TabBar.java
index 14b1845..4f179b0 100644
--- a/src/com/android/browser/TabBar.java
+++ b/src/com/android/browser/TabBar.java
@@ -22,7 +22,13 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Shader;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
@@ -74,6 +80,17 @@
     private Drawable mGenericFavicon;
     private String mLoadingText;
 
+    private Drawable mActiveDrawable;
+    private Drawable mInactiveDrawable;
+
+    private Bitmap mShaderBuffer;
+    private Canvas mShaderCanvas;
+    private Paint mShaderPaint;
+    private int mTabHeight;
+    private int mTabOverlap;
+    private int mTabSliceWidth;
+    private int mTabPadding;
+
     public TabBar(Activity activity, UiController controller, BaseUi ui) {
         super(activity);
         mActivity = activity;
@@ -83,16 +100,20 @@
         Resources res = activity.getResources();
         mTabWidthSelected = (int) res.getDimension(R.dimen.tab_width_selected);
         mTabWidthUnselected = (int) res.getDimension(R.dimen.tab_width_unselected);
+        mActiveDrawable = res.getDrawable(R.drawable.bg_urlbar);
+        mInactiveDrawable = res.getDrawable(R.drawable.browsertab_inactive);
 
         mTabMap = new HashMap<Tab, TabViewData>();
         Resources resources = activity.getResources();
         LayoutInflater factory = LayoutInflater.from(activity);
         factory.inflate(R.layout.tab_bar, this);
+        setPadding(12, 12, 0, 0);
         mTabs = (TabScrollView) findViewById(R.id.tabs);
         mNewTab = (ImageButton) findViewById(R.id.newtab);
         mNewTab.setOnClickListener(this);
         mGenericFavicon = res.getDrawable(R.drawable.app_web_browser_sm);
         mLoadingText = res.getString(R.string.title_bar_loading);
+        setChildrenDrawingOrderEnabled(true);
 
         // TODO: Change enabled states based on whether you can go
         // back/forward.  Probably should be done inside onPageStarted.
@@ -102,6 +123,22 @@
         mUserRequestedUrlbar = false;
         mTitleVisible = true;
         mButtonWidth = -1;
+        // tab dimensions
+        mTabHeight = (int) res.getDimension(R.dimen.tab_height);
+        mTabOverlap = (int) res.getDimension(R.dimen.tab_overlap);
+        mTabSliceWidth = (int) res.getDimension(R.dimen.tab_slice);
+        mTabPadding = (int) res.getDimension(R.dimen.tab_padding);
+        int maxTabWidth = (int) res.getDimension(R.dimen.max_tab_width);
+        // shader initialization
+        mShaderBuffer = Bitmap.createBitmap(maxTabWidth, mTabHeight,
+                Bitmap.Config.ARGB_8888);
+        mShaderCanvas = new Canvas(mShaderBuffer);
+        mShaderPaint = new Paint();
+        BitmapShader shader = new BitmapShader(mShaderBuffer,
+                Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+        mShaderPaint.setShader(shader);
+        mShaderPaint.setStyle(Paint.Style.FILL);
+        mShaderPaint.setAntiAlias(true);
     }
 
     void updateTabs(List<Tab> tabs) {
@@ -115,17 +152,31 @@
     }
 
     @Override
+    protected void onMeasure(int hspec, int vspec) {
+        super.onMeasure(hspec, vspec);
+        int w = getMeasuredWidth();
+        // adjust for new tab overlap
+        w -= mTabOverlap;
+        setMeasuredDimension(w, getMeasuredHeight());
+    }
+
+    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        // use paddingLeft and paddingTop
+        int pl = getPaddingLeft();
+        int pt = getPaddingTop();
         if (mButtonWidth == -1) {
-            mButtonWidth = mNewTab.getMeasuredWidth();
+            mButtonWidth = mNewTab.getMeasuredWidth() - mTabOverlap;
         }
         int sw = mTabs.getMeasuredWidth();
-        int w = right-left;
+        int w = right - left - pl;
         if (w-sw < mButtonWidth) {
             sw = w - mButtonWidth;
         }
-        mTabs.layout(0, 0, sw, bottom-top );
-        mNewTab.layout(sw, 0, sw+mButtonWidth, bottom-top);
+        mTabs.layout(pl, pt, pl + sw, bottom - top);
+        // adjust for overlap
+        mNewTab.layout(pl + sw - mTabOverlap, pt,
+                pl + sw + mButtonWidth - mTabOverlap, bottom - top);
     }
 
     public void onClick(View view) {
@@ -212,6 +263,12 @@
         return tv;
     }
 
+    @Override
+    protected int getChildDrawingOrder(int count, int i) {
+        // reverse
+        return count - 1 - i;
+    }
+
     /**
      * View used in the tab bar
      */
@@ -226,16 +283,21 @@
         ImageView mClose;
         boolean mSelected;
         boolean mInLoad;
+        Path mPath;
+        int[] mWindowPos;
 
         /**
          * @param context
          */
         public TabView(Context context, TabViewData tab) {
             super(context);
+            setWillNotDraw(false);
+            mPath = new Path();
+            mWindowPos = new int[2];
             mTabData = tab;
             setGravity(Gravity.CENTER_VERTICAL);
             setOrientation(LinearLayout.HORIZONTAL);
-            setBackgroundResource(R.drawable.tab_background);
+            setPadding(0, 0, mTabPadding, 0);
             LayoutInflater inflater = LayoutInflater.from(getContext());
             mTabContent = inflater.inflate(R.layout.tab_title, this, true);
             mTitle = (TextView) mTabContent.findViewById(R.id.title);
@@ -286,11 +348,11 @@
             mTitle.setTextAppearance(mActivity, mSelected ?
                     R.style.TabTitleSelected : R.style.TabTitleUnselected);
             setHorizontalFadingEdgeEnabled(!mSelected);
-            setFadingEdgeLength(50);
             super.setActivated(selected);
-            setLayoutParams(new LayoutParams(selected ?
-                    mTabWidthSelected : mTabWidthUnselected,
-                    LayoutParams.MATCH_PARENT));
+            LayoutParams lp = (LinearLayout.LayoutParams) getLayoutParams();
+            lp.width = selected ? mTabWidthSelected : mTabWidthUnselected;
+            lp.height =  LayoutParams.MATCH_PARENT;
+            setLayoutParams(lp);
         }
 
         void setDisplayTitle(String title) {
@@ -328,6 +390,42 @@
             }
         }
 
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+            super.onLayout(changed, l, t, r, b);
+            setTabPath(mPath, 0, 0, r - l, b - t);
+        }
+
+        @Override
+        protected void dispatchDraw(Canvas canvas) {
+            int state = canvas.save();
+            int[] pos = new int[2];
+            getLocationInWindow(mWindowPos);
+            Drawable drawable = mSelected ? mActiveDrawable : mInactiveDrawable;
+            drawable.setBounds(0, 0, mUi.getTitleBarWidth(), getHeight());
+            drawClipped(canvas, drawable, mPath, mWindowPos[0]);
+            canvas.restoreToCount(state);
+            super.dispatchDraw(canvas);
+        }
+
+        private void drawClipped(Canvas canvas, Drawable drawable,
+                Path clipPath, int left) {
+            mShaderCanvas.drawColor(Color.TRANSPARENT);
+            mShaderCanvas.translate(-left, 0);
+            drawable.draw(mShaderCanvas);
+            canvas.drawPath(clipPath, mShaderPaint);
+            mShaderCanvas.translate(left, 0);
+        }
+
+        private void setTabPath(Path path, int l, int t, int r, int b) {
+            path.reset();
+            path.moveTo(l, b);
+            path.lineTo(l, t);
+            path.lineTo(r - mTabSliceWidth, t);
+            path.lineTo(r, b);
+            path.close();
+        }
+
     }
 
     /**
diff --git a/src/com/android/browser/TabScrollView.java b/src/com/android/browser/TabScrollView.java
index fbb40aa..04ed5a3 100644
--- a/src/com/android/browser/TabScrollView.java
+++ b/src/com/android/browser/TabScrollView.java
@@ -17,10 +17,8 @@
 package com.android.browser;
 
 import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
@@ -41,6 +39,7 @@
     private Drawable mArrowLeft;
     private Drawable mArrowRight;
     private int mAnimationDuration;
+    private int mTabOverlap;
 
     /**
      * @param context
@@ -73,8 +72,10 @@
         mContext = ctx;
         mAnimationDuration = ctx.getResources().getInteger(
                 R.integer.tab_animation_duration);
+        mTabOverlap = (int) ctx.getResources().getDimension(R.dimen.tab_overlap);
         setHorizontalScrollBarEnabled(false);
-        mContentView = new LinearLayout(mContext);
+        setOverScrollMode(OVER_SCROLL_NEVER);
+        mContentView = new TabLayout(mContext);
         mContentView.setOrientation(LinearLayout.HORIZONTAL);
         mContentView.setLayoutParams(
                 new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
@@ -211,4 +212,49 @@
         return getScrollX();
     }
 
+    class TabLayout extends LinearLayout {
+
+        public TabLayout(Context context) {
+            super(context);
+            setChildrenDrawingOrderEnabled(true);
+        }
+
+        @Override
+        protected void onMeasure(int hspec, int vspec) {
+            super.onMeasure(hspec, vspec);
+            int w = getMeasuredWidth();
+            w -= Math.max(0, mContentView.getChildCount() - 1) * mTabOverlap;
+            setMeasuredDimension(w, getMeasuredHeight());
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            super.onLayout(changed, left, top, right, bottom);
+            if (getChildCount() > 1) {
+                int nextLeft = getChildAt(0).getRight() - mTabOverlap;
+                for (int i = 1; i < getChildCount(); i++) {
+                    View tab = getChildAt(i);
+                    int w = tab.getRight() - tab.getLeft();
+                    tab.layout(nextLeft, tab.getTop(), nextLeft + w, tab.getBottom());
+                    nextLeft += w - mTabOverlap;
+                }
+            }
+        }
+
+        @Override
+        protected int getChildDrawingOrder(int count, int i) {
+            int next = -1;
+            if ((i == (count - 1)) && (mSelected >= 0)) {
+                next = mSelected;
+            } else {
+                next = count - i - 1;
+                if (next <= mSelected) {
+                    next--;
+                }
+            }
+            return next;
+        }
+
+    }
+
 }
diff --git a/src/com/android/browser/UI.java b/src/com/android/browser/UI.java
index 2bfec44..b56ba30 100644
--- a/src/com/android/browser/UI.java
+++ b/src/com/android/browser/UI.java
@@ -131,4 +131,7 @@
     View getVideoLoadingProgressView();
 
     void bookmarkedStatusHasChanged(Tab tab);
+
+    void showMaxTabsWarning();
+
 }
diff --git a/src/com/android/browser/provider/BrowserProvider2.java b/src/com/android/browser/provider/BrowserProvider2.java
index 8d9f1fe..8137d55 100644
--- a/src/com/android/browser/provider/BrowserProvider2.java
+++ b/src/com/android/browser/provider/BrowserProvider2.java
@@ -622,12 +622,6 @@
             }
 
             case BOOKMARKS_FOLDER: {
-                // Don't allow selections to be applied to the default folder
-                if (!TextUtils.isEmpty(selection) || selectionArgs != null) {
-                    throw new UnsupportedOperationException(
-                            "selections aren't supported on this URI");
-                }
-
                 // Look for an account
                 boolean useAccount = false;
                 String accountType = uri.getQueryParameter(Bookmarks.PARAM_ACCOUNT_TYPE);
@@ -649,37 +643,49 @@
                 }
                 if (!useAccount) {
                     qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP);
-                    query = qb.buildQuery(projection,
-                            Bookmarks.PARENT + "=? AND " + Bookmarks.IS_DELETED + "=0",
-                            null, null, null, sortOrder, null);
-
+                    String where = Bookmarks.PARENT + "=? AND " + Bookmarks.IS_DELETED + "=0";
+                    where = DatabaseUtils.concatenateWhere(where, selection);
                     args = new String[] { Long.toString(FIXED_ID_ROOT) };
+                    if (selectionArgs != null) {
+                        args = DatabaseUtils.appendSelectionArgs(args, selectionArgs);
+                    }
+                    query = qb.buildQuery(projection, where, null, null, sortOrder, null);
                 } else {
                     qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP);
+                    String where = Bookmarks.ACCOUNT_TYPE + "=? AND " +
+                            Bookmarks.ACCOUNT_NAME + "=? " +
+                            "AND parent = " +
+                            "(SELECT _id FROM " + TABLE_BOOKMARKS + " WHERE " +
+                            ChromeSyncColumns.SERVER_UNIQUE + "=" +
+                            "'" + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR + "' " +
+                            "AND account_type = ? AND account_name = ?) " +
+                            "AND " + Bookmarks.IS_DELETED + "=0";
+                    where = DatabaseUtils.concatenateWhere(where, selection);
                     String bookmarksBarQuery = qb.buildQuery(projection,
-                            Bookmarks.ACCOUNT_TYPE + "=? AND " + Bookmarks.ACCOUNT_NAME + "=? " +
-                                    "AND parent = " +
-                                        "(SELECT _id FROM " + TABLE_BOOKMARKS + " WHERE " +
-                                        ChromeSyncColumns.SERVER_UNIQUE + "=" +
-                                        "'" + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR + "' " +
-                                        "AND account_type = ? AND account_name = ?) " +
-                                    "AND " + Bookmarks.IS_DELETED + "=0",
-                            null, null, null, null, null);
+                            where, null, null, null, null);
+                    args = new String[] {accountType, accountName,
+                            accountType, accountName};
+                    if (selectionArgs != null) {
+                        args = DatabaseUtils.appendSelectionArgs(args, selectionArgs);
+                    }
 
+                    where = Bookmarks.ACCOUNT_TYPE + "=? AND " + Bookmarks.ACCOUNT_NAME + "=?" +
+                            " AND " + ChromeSyncColumns.SERVER_UNIQUE + "=?";
+                    where = DatabaseUtils.concatenateWhere(where, selection);
                     qb.setProjectionMap(OTHER_BOOKMARKS_PROJECTION_MAP);
                     String otherBookmarksQuery = qb.buildQuery(projection,
-                            Bookmarks.ACCOUNT_TYPE + "=? AND " + Bookmarks.ACCOUNT_NAME + "=?" +
-                                    " AND " + ChromeSyncColumns.SERVER_UNIQUE + "=?",
-                            null, null, null, null, null);
+                            where, null, null, null, null);
 
                     query = qb.buildUnionQuery(
                             new String[] { bookmarksBarQuery, otherBookmarksQuery },
                             sortOrder, limit);
 
-                    args = new String[] {
-                            accountType, accountName, accountType, accountName,
+                    args = DatabaseUtils.appendSelectionArgs(args, new String[] {
                             accountType, accountName, ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS,
-                            };
+                            });
+                    if (selectionArgs != null) {
+                        args = DatabaseUtils.appendSelectionArgs(args, selectionArgs);
+                    }
                 }
 
                 Cursor cursor = db.rawQuery(query, args);
@@ -697,6 +703,7 @@
                 // fall through
             }
             case HISTORY: {
+                filterSearchClient(selectionArgs);
                 if (sortOrder == null) {
                     sortOrder = DEFAULT_SORT_HISTORY;
                 }
@@ -803,6 +810,7 @@
                 // fall through
             }
             case HISTORY: {
+                filterSearchClient(selectionArgs);
                 return db.delete(TABLE_HISTORY, selection, selectionArgs);
             }
 
@@ -877,6 +885,9 @@
                 if (!values.containsKey(History.DATE_CREATED)) {
                     values.put(History.DATE_CREATED, System.currentTimeMillis());
                 }
+                String url = values.getAsString(History.URL);
+                url = filterSearchClient(url);
+                values.put(History.URL, url);
 
                 // Extract out the image values so they can be inserted into the images table
                 ContentValues imageValues = extractImageValues(values,
@@ -917,6 +928,32 @@
         }
     }
 
+    private void filterSearchClient(String[] selectionArgs) {
+        if (selectionArgs != null) {
+            for (int i = 0; i < selectionArgs.length; i++) {
+                selectionArgs[i] = filterSearchClient(selectionArgs[i]);
+            }
+        }
+    }
+
+    // Filters out the client=ms- param for search urls
+    private String filterSearchClient(String url) {
+        // 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);
+            }
+        }
+        return url;
+    }
+
     /**
      * Searches are unique, so perform an UPSERT manually since SQLite doesn't support them.
      */
@@ -1086,6 +1123,7 @@
     int updateHistoryInTransaction(ContentValues values, String selection, String[] selectionArgs) {
         int count = 0;
         final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        filterSearchClient(selectionArgs);
         Cursor cursor = query(History.CONTENT_URI,
                 new String[] { History._ID, History.URL },
                 selection, selectionArgs, null);
@@ -1095,7 +1133,8 @@
             boolean updatingUrl = values.containsKey(History.URL);
             String url = null;
             if (updatingUrl) {
-                url = values.getAsString(History.URL);
+                url = filterSearchClient(values.getAsString(History.URL));
+                values.put(History.URL, url);
             }
             ContentValues imageValues = extractImageValues(values, url);