Bug 899393 - Switch to a tab if it's already open. r=wesj,lucasr
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Mon, 12 Aug 2013 11:44:46 -0700
changeset 143521 2b52431a80543125d1b3c278b5575cb7cf9fddc7
parent 143520 dff6e98eaac0bfd79785f97273f9e9612ab92481
child 143522 673e9806d6c302d5cda2c784106cb277ac49371a
push id25130
push userlrocha@mozilla.com
push dateWed, 21 Aug 2013 09:41:27 +0000
treeherdermozilla-central@b2486721572e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswesj, lucasr
bugs899393
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 899393 - Switch to a tab if it's already open. r=wesj,lucasr
mobile/android/base/BrowserApp.java
mobile/android/base/Tabs.java
mobile/android/base/home/BookmarksListView.java
mobile/android/base/home/BrowserSearch.java
mobile/android/base/home/HomePager.java
mobile/android/base/home/MostRecentPage.java
mobile/android/base/home/MostVisitedPage.java
mobile/android/base/home/ReadingListPage.java
mobile/android/base/home/SearchEngineRow.java
mobile/android/base/home/TopBookmarksView.java
mobile/android/base/home/TwoLinePageRow.java
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -11,16 +11,17 @@ import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.GeckoLayerClient;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.gfx.PanZoomController;
 import org.mozilla.gecko.health.BrowserHealthReporter;
 import org.mozilla.gecko.home.BrowserSearch;
 import org.mozilla.gecko.home.HomePager;
+import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.FloatUtils;
 import org.mozilla.gecko.util.GamepadUtils;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.util.UiAsyncTask;
 import org.mozilla.gecko.widget.GeckoActionProvider;
@@ -79,17 +80,17 @@ import java.util.Vector;
 abstract public class BrowserApp extends GeckoApp
                                  implements TabsPanel.TabsLayoutChangeListener,
                                             PropertyAnimator.PropertyAnimationListener,
                                             View.OnKeyListener,
                                             GeckoLayerClient.OnMetricsChangedListener,
                                             BrowserSearch.OnSearchListener,
                                             BrowserSearch.OnEditSuggestionListener,
                                             HomePager.OnNewTabsListener,
-                                            HomePager.OnUrlOpenListener {
+                                            OnUrlOpenListener {
     private static final String LOGTAG = "GeckoBrowserApp";
 
     private static final String PREF_CHROME_DYNAMICTOOLBAR = "browser.chrome.dynamictoolbar";
 
     private static final String ABOUT_HOME = "about:home";
 
     private static final int TABS_ANIMATION_DURATION = 450;
 
@@ -1257,16 +1258,45 @@ abstract public class BrowserApp extends
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         mToast.onSaveInstanceState(outState);
         outState.putBoolean(STATE_DYNAMIC_TOOLBAR_ENABLED, mDynamicToolbarEnabled);
         outState.putInt(STATE_ABOUT_HOME_TOP_PADDING, mHomePagerContainer.getPaddingTop());
     }
 
+    /**
+     * Attempts to switch to an open tab with the given URL.
+     *
+     * @return true if we successfully switched to a tab, false otherwise.
+     */
+    private boolean maybeSwitchToTab(String url, EnumSet<OnUrlOpenListener.Flags> flags) {
+        if (!flags.contains(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB)) {
+            return false;
+        }
+
+        final Tabs tabs = Tabs.getInstance();
+        final int tabId = tabs.getTabIdForUrl(url);
+        if (tabId < 0) {
+            return false;
+        }
+
+        // If this tab is already selected, just hide the home pager.
+        if (tabs.isSelectedTab(tabs.getTab(tabId))) {
+            hideHomePager();
+        } else {
+            tabs.selectTab(tabId);
+        }
+
+        hideBrowserSearch();
+        mBrowserToolbar.cancelEdit();
+
+        return true;
+    }
+
     private void openUrl(String url) {
         openUrl(url, null, false);
     }
 
     private void openUrl(String url, boolean newTab) {
         openUrl(url, null, newTab);
     }
 
@@ -2060,20 +2090,22 @@ abstract public class BrowserApp extends
     public void onNewTabs(String[] urls) {
         for (String url : urls) {
             openUrl(url, true);
         }
     }
 
     // HomePager.OnUrlOpenListener
     @Override
-    public void onUrlOpen(String url) {
-        openUrl(url);
+    public void onUrlOpen(String url, EnumSet<OnUrlOpenListener.Flags> flags) {
+        if (!maybeSwitchToTab(url, flags)) {
+            openUrl(url);
+        }
     }
- 
+
     // BrowserSearch.OnSearchListener
     @Override
     public void onSearch(String engineId, String text) {
         openUrl(text, engineId);
     }
 
     // BrowserSearch.OnEditSuggestionListener
     @Override
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -580,29 +580,29 @@ public class Tabs implements GeckoEventL
         backgroundHandler.postDelayed(mPersistTabsRunnable, PERSIST_TABS_AFTER_MILLISECONDS);
     }
 
     private void registerEventListener(String event) {
         GeckoAppShell.getEventDispatcher().registerEventListener(event, this);
     }
 
     /**
-     * Returns true if any of the tabs has the requested url.
-     * 
-     * @return true if the url is open currently, false otherwise.
+     * Looks for an open tab with the given URL.
+     *
+     * @return id of an open tab with the given URL; -1 if the tab doesn't exist.
      */
-    public boolean hasUrl(String url) {
+    public int getTabIdForUrl(String url) {
         for (Tab tab : mOrder) {
             if (TextUtils.equals(tab.getURL(), url) ||
                 TextUtils.equals(ReaderModeUtils.getUrlFromAboutReader(tab.getURL()), url)) {
-                return true;
+                return tab.getId();
             }
         }
 
-        return false;
+        return -1;
     }
 
     /**
      * Loads a tab with the given URL in the currently selected tab.
      *
      * @param url URL of page to load, or search term used if searchEngine is given
      */
     public void loadUrl(String url) {
--- a/mobile/android/base/home/BookmarksListView.java
+++ b/mobile/android/base/home/BookmarksListView.java
@@ -16,16 +16,18 @@ import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.widget.AdapterView;
 import android.widget.HeaderViewListAdapter;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 
+import java.util.EnumSet;
+
 /**
  * A ListView of bookmarks.
  */
 public class BookmarksListView extends HomeListView
                                implements AdapterView.OnItemClickListener{
     public static final String LOGTAG = "GeckoBookmarksListView";
 
     // The last motion event that was intercepted.
@@ -129,15 +131,14 @@ public class BookmarksListView extends H
         if (type == Bookmarks.TYPE_FOLDER) {
             // If we're clicking on a folder, update adapter to move to that folder
             final int folderId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
             final String folderTitle = adapter.getFolderTitle(parent.getContext(), cursor);
             adapter.moveToChildFolder(folderId, folderTitle);
         } else {
             // Otherwise, just open the URL
             final String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
-            OnUrlOpenListener listener = getOnUrlOpenListener();
-            if (listener != null) {
-                listener.onUrlOpen(url);
-            }
+
+            // This item is a TwoLinePageRow, so we allow switch-to-tab.
+            getOnUrlOpenListener().onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
         }
     }
 }
--- a/mobile/android/base/home/BrowserSearch.java
+++ b/mobile/android/base/home/BrowserSearch.java
@@ -46,16 +46,17 @@ import android.view.animation.Accelerate
 import android.view.animation.Animation;
 import android.view.animation.TranslateAnimation;
 import android.widget.AdapterView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 
 import java.util.ArrayList;
+import java.util.EnumSet;
 import java.util.List;
 
 /**
  * Fragment that displays frecency search results in a ListView.
  */
 public class BrowserSearch extends HomeFragment
                            implements GeckoEventListener {
     // Logging tag name
@@ -225,17 +226,19 @@ public class BrowserSearch extends HomeF
 
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 // Account for the search engines
                 position -= getSuggestEngineCount();
                 final Cursor c = mAdapter.getCursor(position);
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
-                mUrlOpenListener.onUrlOpen(url);
+
+                // This item is a TwoLinePageRow, so we allow switch-to-tab.
+                mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         final ListSelectionListener listener = new ListSelectionListener();
         mList.setOnItemSelectedListener(listener);
         mList.setOnFocusChangeListener(listener);
 
         mList.setOnKeyListener(new View.OnKeyListener() {
--- a/mobile/android/base/home/HomePager.java
+++ b/mobile/android/base/home/HomePager.java
@@ -35,17 +35,21 @@ public class HomePager extends ViewPager
         HISTORY,
         BOOKMARKS,
         READING_LIST
     }
 
     private EnumMap<Page, Fragment> mPages = new EnumMap<Page, Fragment>(Page.class);
 
     public interface OnUrlOpenListener {
-        public void onUrlOpen(String url);
+        public enum Flags {
+            ALLOW_SWITCH_TO_TAB
+        }
+
+        public void onUrlOpen(String url, EnumSet<Flags> flags);
     }
 
     public interface OnNewTabsListener {
         public void onNewTabs(String[] urls);
     }
 
     public HomePager(Context context) {
         this(context, null);
--- a/mobile/android/base/home/MostRecentPage.java
+++ b/mobile/android/base/home/MostRecentPage.java
@@ -24,16 +24,17 @@ import android.view.View;
 import android.view.ViewStub;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.TextView;
 
 import java.util.Date;
+import java.util.EnumSet;
 
 /**
  * Fragment that displays recent history in a ListView.
  */
 public class MostRecentPage extends HomeFragment {
     // Logging tag name
     private static final String LOGTAG = "GeckoMostRecentPage";
 
@@ -96,17 +97,19 @@ public class MostRecentPage extends Home
 
         mList = (ListView) view.findViewById(R.id.list);
         mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 position -= mAdapter.getMostRecentSectionsCountBefore(position);
                 final Cursor c = mAdapter.getCursor(position);
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
-                mUrlOpenListener.onUrlOpen(url);
+
+                // This item is a TwoLinePageRow, so we allow switch-to-tab.
+                mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         registerForContextMenu(mList);
     }
 
     @Override
     public void onDestroyView() {
--- a/mobile/android/base/home/MostVisitedPage.java
+++ b/mobile/android/base/home/MostVisitedPage.java
@@ -20,16 +20,18 @@ import android.support.v4.content.Loader
 import android.support.v4.widget.CursorAdapter;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ListView;
 import android.widget.TextView;
 
+import java.util.EnumSet;
+
 /**
  * Fragment that displays frecency search results in a ListView.
  */
 public class MostVisitedPage extends HomeFragment {
     // Logging tag name
     private static final String LOGTAG = "GeckoMostVisitedPage";
 
     // Cursor loader ID for search query
@@ -90,17 +92,19 @@ public class MostVisitedPage extends Hom
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 final Cursor c = mAdapter.getCursor();
                 if (c == null || !c.moveToPosition(position)) {
                     return;
                 }
 
                 final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
-                mUrlOpenListener.onUrlOpen(url);
+
+                // This item is a TwoLinePageRow, so we allow switch-to-tab.
+                mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         registerForContextMenu(mList);
     }
 
     @Override
     public void onDestroyView() {
--- a/mobile/android/base/home/ReadingListPage.java
+++ b/mobile/android/base/home/ReadingListPage.java
@@ -21,16 +21,18 @@ import android.support.v4.app.LoaderMana
 import android.support.v4.content.Loader;
 import android.support.v4.widget.CursorAdapter;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ListView;
 
+import java.util.EnumSet;
+
 /**
  * Fragment that displays reading list contents in a ListView.
  */
 public class ReadingListPage extends HomeFragment {
     // Cursor loader ID for reading list
     private static final int LOADER_ID_READING_LIST = 0;
 
     // Adapter for the list of reading list items
@@ -81,17 +83,19 @@ public class ReadingListPage extends Hom
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 final Cursor c = mAdapter.getCursor();
                 if (c == null || !c.moveToPosition(position)) {
                     return;
                 }
 
                 String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
                 url = ReaderModeUtils.getAboutReaderForUrl(url, true);
-                mUrlOpenListener.onUrlOpen(url);
+
+                // This item is a TwoLinePageRow, so we allow switch-to-tab.
+                mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
             }
         });
 
         registerForContextMenu(mList);
     }
 
     @Override
     public void onDestroyView() {
--- a/mobile/android/base/home/SearchEngineRow.java
+++ b/mobile/android/base/home/SearchEngineRow.java
@@ -19,16 +19,18 @@ import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.animation.AlphaAnimation;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import java.util.EnumSet;
+
 class SearchEngineRow extends AnimatedHeightLayout {
     // Duration for fade-in animation
     private static final int ANIMATION_DURATION = 250;
 
     // Inner views
     private final FlowLayout mSuggestionView;
     private final FaviconView mIconView;
     private final LinearLayout mUserEnteredView;
@@ -72,17 +74,17 @@ class SearchEngineRow extends AnimatedHe
             public void onClick(View v) {
                 final String suggestion = getSuggestionTextFromView(v);
 
                 // If we're not clicking the user-entered view (the first suggestion item)
                 // and the search matches a URL pattern, go to that URL. Otherwise, do a
                 // search for the term.
                 if (v != mUserEnteredView && !StringUtils.isSearchQuery(suggestion, false)) {
                     if (mUrlOpenListener != null) {
-                        mUrlOpenListener.onUrlOpen(suggestion);
+                        mUrlOpenListener.onUrlOpen(suggestion, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
                     }
                 } else if (mSearchListener != null) {
                     mSearchListener.onSearch(mSearchEngine.name, suggestion);
                 }
             }
         };
 
         mLongClickListener = new OnLongClickListener() {
--- a/mobile/android/base/home/TopBookmarksView.java
+++ b/mobile/android/base/home/TopBookmarksView.java
@@ -19,16 +19,18 @@ import android.util.AttributeSet;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.GridLayoutAnimationController;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.GridView;
 
+import java.util.EnumSet;
+
 /**
  * A grid view of top bookmarks and pinned tabs.
  * Each cell in the grid is a TopBookmarkItemView.
  */
 public class TopBookmarksView extends GridView {
     private static final String LOGTAG = "GeckoTopBookmarksView";
 
     // Listener for pinning bookmarks.
@@ -89,17 +91,17 @@ public class TopBookmarksView extends Gr
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 TopBookmarkItemView row = (TopBookmarkItemView) view;
                 String url = row.getUrl();
 
                 // If the url is empty, the user can pin a bookmark.
                 // If not, navigate to the page given by the url.
                 if (!TextUtils.isEmpty(url)) {
                     if (mUrlOpenListener != null) {
-                        mUrlOpenListener.onUrlOpen(url);
+                        mUrlOpenListener.onUrlOpen(url, EnumSet.noneOf(OnUrlOpenListener.Flags.class));
                     }
                 } else {
                     if (mPinBookmarkListener != null) {
                         mPinBookmarkListener.onPinBookmark(position);
                     }
                 }
             }
         });
--- a/mobile/android/base/home/TwoLinePageRow.java
+++ b/mobile/android/base/home/TwoLinePageRow.java
@@ -129,17 +129,18 @@ public class TwoLinePageRow extends Line
         mPageUrl = url;
         updateDisplayedUrl();
     }
 
     /**
      * Replaces the page URL with "Switch to tab" if there is already a tab open with that URL.
      */
     private void updateDisplayedUrl() {
-        if (!mShowIcons || !Tabs.getInstance().hasUrl(mPageUrl)) {
+        int tabId = Tabs.getInstance().getTabIdForUrl(mPageUrl);
+        if (!mShowIcons || tabId < 0) {
             setUrl(mPageUrl);
             setUrlIcon(NO_ICON);
         } else {
             setUrl(R.string.switch_to_tab);
             setUrlIcon(R.drawable.ic_url_bar_tab);
         }
     }