Bug 465673 - Open related tabs next to the current one. r=dao, ui-r=beltzner
authorJohn Morkel <jmorkel@gmail.com>
Fri, 04 Sep 2009 16:37:17 +0200
changeset 31577 6de448deb9551790c8405463cee73ffb9260ded5
parent 31576 59d62d5e33c7405a9e1f2d73eacdff2aa3b3f7f6
child 31578 0333691ae0dbddb80fd84841147c2e1f2339731f
push id95
push userdgottwald@mozilla.com
push dateWed, 09 Sep 2009 10:18:36 +0000
reviewersdao, beltzner
bugs465673
milestone1.9.2a2pre
Bug 465673 - Open related tabs next to the current one. r=dao, ui-r=beltzner
browser/app/profile/firefox.js
browser/base/content/tabbrowser.xml
browser/base/content/test/Makefile.in
browser/base/content/test/browser_relatedTabs.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -321,16 +321,17 @@ pref("browser.link.open_newwindow.restri
 
 // Tabbed browser
 #ifndef WINCE
 pref("browser.tabs.autoHide", false);
 #else
 pref("browser.tabs.autoHide", true);
 #endif
 pref("browser.tabs.closeWindowWithLastTab", true);
+pref("browser.tabs.insertRelatedAfterCurrent", true);
 pref("browser.tabs.warnOnClose", true);
 pref("browser.tabs.warnOnOpen", true);
 pref("browser.tabs.maxOpenBeforeWarn", 15);
 pref("browser.tabs.loadInBackground", true);
 pref("browser.tabs.loadFolderAndReplace", true);
 pref("browser.tabs.opentabfor.middleclick", true);
 pref("browser.tabs.loadDivertedInBackground", false);
 pref("browser.tabs.loadBookmarksInBackground", false);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -183,16 +183,19 @@
         document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
       </field>
       <field name="mUndoCloseTabMenuItem">
         document.getAnonymousElementByAttribute(this, "anonid", "undoCloseTabMenuItem");
       </field>
       <field name="mCurrentTab">
         null
       </field>
+      <field name="_lastRelatedTab">
+        null
+      </field>
       <field name="mCurrentBrowser">
         null
       </field>
       <field name="mProgressListeners">
         []
       </field>
       <field name="mTabsProgressListeners">
         []
@@ -799,16 +802,18 @@
           <![CDATA[
             var newBrowser = this.getBrowserAtIndex(this.mTabContainer.selectedIndex);
             if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
               return;
 
             if (this.mCurrentTab != this.selectedTab)
               this.mCurrentTab.owner = null;
 
+            this._lastRelatedTab = null;
+
             var fm = Components.classes["@mozilla.org/focus-manager;1"].
                        getService(Components.interfaces.nsIFocusManager);
             var focusedChromeElement = fm.getFocusedElementForWindow(window, false, {});
 
             var oldBrowser = this.mCurrentBrowser;
             if (oldBrowser)
               oldBrowser.setAttribute("type", "content-targetable");
 
@@ -1283,16 +1288,28 @@
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var evt = document.createEvent("Events");
             evt.initEvent("TabOpen", true, false);
             t.dispatchEvent(evt);
 
+            // Check if we're opening a tab related to the current tab and
+            // move it to after the current tab.
+            // aReferrerURI is null or undefined if the tab is opened from
+            // an external application or bookmark, i.e. somewhere other
+            // than the current tab.
+            if (aReferrerURI && this.mPrefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
+              let newTabPos = (this._lastRelatedTab ||
+                               this.selectedTab)._tPos + 1;
+              this.moveTabTo(t, newTabPos);
+              this._lastRelatedTab = t;
+            }
+
             return t;
           ]]>
         </body>
       </method>
 
       <method name="warnAboutClosingTabs">
       <parameter name="aAll"/>
       <body>
@@ -1468,16 +1485,18 @@
       <method name="_endRemoveTab">
         <parameter name="args"/>
         <body>
           <![CDATA[
             if (!args)
               return;
             var [aTab, aCloseWindow, aNewTab] = args;
 
+            this._lastRelatedTab = null;
+
             // update the UI early for responsiveness
             aTab.collapsed = true;
             if (aNewTab)
               this.addTab("about:blank");
             this.tabContainer._fillTrailingGap();
             this._blurTab(aTab);
 
             this._removingTabs.splice(this._removingTabs.indexOf(aTab), 1);
@@ -2200,16 +2219,17 @@
         </body>
       </method>
 
       <method name="moveTabTo">
         <parameter name="aTab"/>
         <parameter name="aIndex"/>
         <body>
         <![CDATA[
+          this._lastRelatedTab = null;
           this._browsers = null; // invalidate cache
           this.mTabFilters.splice(aIndex, 0, this.mTabFilters.splice(aTab._tPos, 1)[0]);
           this.mTabListeners.splice(aIndex, 0, this.mTabListeners.splice(aTab._tPos, 1)[0]);
 
           var oldPosition = aTab._tPos;
 
           aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
           this.mCurrentTab._selected = false;
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -129,16 +129,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_bug479408_sample.html \
                  browser_scope.js \
                  browser_overflowScroll.js \
                  browser_sanitizeDialog.js \
                  browser_tabs_owner.js \
                  browser_bug491431.js \
                  browser_bug304198.js \
                  browser_drag.js \
+                 browser_relatedTabs.js \
     $(NULL)
 
 ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
 _BROWSER_FILES += browser_bug462289.js
 else
 _BROWSER_FILES += browser_customize.js
 endif
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_relatedTabs.js
@@ -0,0 +1,84 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is browser test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Simon B├╝nzli <zeniko@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   John Morkel <jmorkel@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function test() {
+  // Add several new tabs in sequence, interrupted by selecting a
+  // different tab, moving a tab around and closing a tab,
+  // returning a list of opened tabs for verifying the expected order.
+  // The new tab behaviour is documented in bug 465673
+  function tabOpenDance() {
+    let tabs = [];
+    function addTab(aURL,aReferrer)
+      tabs.push(gBrowser.addTab(aURL, aReferrer));
+
+    addTab("http://localhost:8888/#0");
+    gBrowser.selectedTab = tabs[0];
+    addTab("http://localhost:8888/#1");
+    addTab("http://localhost:8888/#2",gBrowser.currentURI);
+    addTab("http://localhost:8888/#3",gBrowser.currentURI);
+    gBrowser.selectedTab = tabs[tabs.length - 1];
+    gBrowser.selectedTab = tabs[0];
+    addTab("http://localhost:8888/#4",gBrowser.currentURI);
+    gBrowser.selectedTab = tabs[3];
+    addTab("http://localhost:8888/#5",gBrowser.currentURI);
+    gBrowser.removeTab(tabs.pop());
+    addTab("about:blank",gBrowser.currentURI);
+    gBrowser.moveTabTo(gBrowser.selectedTab, 1);
+    addTab("http://localhost:8888/#6",gBrowser.currentURI);
+    addTab();
+    addTab("http://localhost:8888/#7");
+
+    return tabs;
+  }
+
+  function cleanUp(aTabs)
+    aTabs.forEach(gBrowser.removeTab, gBrowser);
+
+  let tabs = tabOpenDance();
+
+  is(tabs[0], gBrowser.mTabs[3], "tab without referrer was opened to the far right");
+  is(tabs[1], gBrowser.mTabs[7], "tab without referrer was opened to the far right");
+  is(tabs[2], gBrowser.mTabs[5], "tab with referrer opened immediately to the right");
+  is(tabs[3], gBrowser.mTabs[1], "next tab with referrer opened further to the right");
+  is(tabs[4], gBrowser.mTabs[4], "tab selection changed, tab opens immediately to the right");
+  is(tabs[5], gBrowser.mTabs[6], "blank tab with referrer opens to the right of 3rd original tab where removed tab was");
+  is(tabs[6], gBrowser.mTabs[2], "tab has moved, new tab opens immediately to the right"); 
+  is(tabs[7], gBrowser.mTabs[8], "blank tab without referrer opens at the end"); 
+  is(tabs[8], gBrowser.mTabs[9], "tab without referrer opens at the end"); 
+
+  cleanUp(tabs);
+}