Bug 1480461 - Add 'Move Tab' sub-menu and 'Move To Start' and 'Move to End' options in tab context menu. r=Felipe
authorJared Wein <jwein@mozilla.com>
Tue, 02 Oct 2018 20:12:28 +0000
changeset 495024 0f5faf5435a815f27817a2944e5d0e83dea0c685
parent 495023 1fa05a1ba7626bbe7e4144b933c490372d818234
child 495025 84b4b9b7586a12ecf26f1d8eeee5a7bdeed9468b
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersFelipe
bugs1480461
milestone64.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 1480461 - Add 'Move Tab' sub-menu and 'Move To Start' and 'Move to End' options in tab context menu. r=Felipe Differential Revision: https://phabricator.services.mozilla.com/D7228
browser/base/content/browser.xul
browser/base/content/tabbrowser.js
browser/locales/en-US/chrome/browser/browser.dtd
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -143,20 +143,38 @@ xmlns="http://www.w3.org/1999/xhtml"
                 oncommand="duplicateTabIn(TabContextMenu.contextTab, 'tab');"/>
       <menu id="context_reopenInContainer"
             label="&reopenInContainer.label;"
             accesskey="&reopenInContainer.accesskey;"
             hidden="true">
         <menupopup oncommand="TabContextMenu.reopenInContainer(event);"
                    onpopupshowing="TabContextMenu.createReopenInContainerMenu(event);"/>
       </menu>
-      <menuitem id="context_openTabInWindow" label="&moveToNewWindow.label;"
-                accesskey="&moveToNewWindow.accesskey;"
-                tbattr="tabbrowser-multiple"
-                oncommand="gBrowser.replaceTabsWithWindow(TabContextMenu.contextTab);"/>
+      <menu id="context_moveTabOptions"
+            multiselectcontextlabel="&moveSelectedTabOptions.label;"
+            multiselectcontextaccesskey="&moveSelectedTabOptions.accesskey;"
+            nonmultiselectcontextlabel="&moveTabOptions.label;"
+            nonmultiselectcontextaccesskey="&moveTabOptions.accesskey;">
+        <menupopup id="moveTabOptionsMenu">
+          <menuitem id="context_moveToStart"
+                    label="&moveToStart.label;"
+                    accesskey="&moveToStart.accesskey;"
+                    tbattr="tabbrowser-multiple"
+                    oncommand="gBrowser.moveTabsToStart(TabContextMenu.contextTab);"/>
+          <menuitem id="context_moveToEnd"
+                    label="&moveToEnd.label;"
+                    accesskey="&moveToEnd.accesskey;"
+                    tbattr="tabbrowser-multiple"
+                    oncommand="gBrowser.moveTabsToEnd(TabContextMenu.contextTab);"/>
+          <menuitem id="context_openTabInWindow" label="&moveToNewWindow.label;"
+                    accesskey="&moveToNewWindow.accesskey;"
+                    tbattr="tabbrowser-multiple"
+                    oncommand="gBrowser.replaceTabsWithWindow(TabContextMenu.contextTab);"/>
+        </menupopup>
+      </menu>
       <menuseparator id="context_sendTabToDevice_separator" class="sync-ui-item"/>
       <menu id="context_sendTabToDevice"
             class="sync-ui-item">
         <menupopup id="context_sendTabToDevicePopupMenu"
                    onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, TabContextMenu.contextTab);"/>
       </menu>
       <menuseparator/>
       <menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2549,16 +2549,40 @@ window._gBrowser = {
     // Additionally send pinned tab events
     if (pinned) {
       this._notifyPinnedStatus(t);
     }
 
     return t;
   },
 
+  moveTabsToStart(contextTab) {
+    let tabs = contextTab.multiselected ?
+      gBrowser.selectedTabs :
+      [contextTab];
+    // Walk the array in reverse order so the tabs are kept in order.
+    for (let i = tabs.length - 1; i >= 0; i--) {
+      let tab = tabs[i];
+      if (tab._tPos > 0) {
+        this.moveTabTo(tab, 0);
+      }
+    }
+  },
+
+  moveTabsToEnd(contextTab) {
+    let tabs = contextTab.multiselected ?
+      gBrowser.selectedTabs :
+      [contextTab];
+    for (let tab of tabs) {
+      if (tab._tPos < this.tabs.length - 1) {
+        this.moveTabTo(tab, this.tabs.length - 1);
+      }
+    }
+  },
+
   warnAboutClosingTabs(tabsToClose, aCloseTabs, aOptionalMessage) {
     if (tabsToClose <= 1)
       return true;
 
     const pref = aCloseTabs == this.closingTabsEnum.ALL ?
       "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
     var shouldPrompt = Services.prefs.getBoolPref(pref);
     if (!shouldPrompt)
@@ -3836,17 +3860,18 @@ window._gBrowser = {
 
   selectAllTabs() {
     let visibleTabs = this.visibleTabs;
     gBrowser.addRangeToMultiSelectedTabs(visibleTabs[0],
                                          visibleTabs[visibleTabs.length - 1]);
   },
 
   allTabsSelected() {
-    return this.visibleTabs.every(t => t.multiselected);
+    return this.visibleTabs.length == 1 ||
+           this.visibleTabs.every(t => t.multiselected);
   },
 
   lockClearMultiSelectionOnce() {
     this._clearMultiSelectionLockedOnce = true;
     this._clearMultiSelectionLocked = true;
   },
 
   unlockClearMultiSelection() {
@@ -5288,16 +5313,33 @@ var TabContextMenu = {
     contextPinTab.hidden = this.contextTab.pinned || multiselectionContext;
     let contextUnpinTab = document.getElementById("context_unpinTab");
     contextUnpinTab.hidden = !this.contextTab.pinned || multiselectionContext;
     let contextPinSelectedTabs = document.getElementById("context_pinSelectedTabs");
     contextPinSelectedTabs.hidden = this.contextTab.pinned || !multiselectionContext;
     let contextUnpinSelectedTabs = document.getElementById("context_unpinSelectedTabs");
     contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !multiselectionContext;
 
+    let contextMoveTabOptions = document.getElementById("context_moveTabOptions");
+    contextMoveTabOptions.disabled = gBrowser.allTabsSelected();
+    let moveTabOptionsStringPrefix = multiselectionContext ? "multiselectcontext" : "nonmultiselectcontext";
+    let moveTabOptionsLabel = contextMoveTabOptions.getAttribute(moveTabOptionsStringPrefix + "label");
+    let moveTabOptionsAccessKey = contextMoveTabOptions.getAttribute(moveTabOptionsStringPrefix + "accesskey");
+    contextMoveTabOptions.setAttribute("label", moveTabOptionsLabel);
+    contextMoveTabOptions.setAttribute("accesskey", moveTabOptionsAccessKey);
+    let selectedTabs = gBrowser.selectedTabs;
+    let contextMoveTabToEnd = document.getElementById("context_moveToEnd");
+    let allSelectedTabsAdjacent = selectedTabs.every((element, index, array) => {
+      return array.length > index + 1 ? element._tPos + 1 == array[index + 1]._tPos : true;
+    });
+    contextMoveTabToEnd.disabled = selectedTabs[selectedTabs.length - 1]._tPos == gBrowser.visibleTabs.length - 1 &&
+                                   allSelectedTabsAdjacent;
+    let contextMoveTabToStart = document.getElementById("context_moveToStart");
+    contextMoveTabToStart.disabled = selectedTabs[0]._tPos == 0 && allSelectedTabsAdjacent;
+
     // Hide the "Duplicate Tab" if there is a selection present
     let contextDuplicateTab = document.getElementById("context_duplicateTab");
     contextDuplicateTab.hidden = multiselectionContext;
 
     // Disable "Close Tabs to the Right" if there are no tabs
     // following it.
     document.getElementById("context_closeTabsToTheEnd").disabled =
       gBrowser.getTabsToTheEndFrom(this.contextTab).length == 0;
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -64,16 +64,26 @@ can reach it easily. -->
 <!ENTITY  pinTab.label                       "Pin Tab">
 <!ENTITY  pinTab.accesskey                   "P">
 <!ENTITY  unpinTab.label                     "Unpin Tab">
 <!ENTITY  unpinTab.accesskey                 "b">
 <!ENTITY  sendPageToDevice.label             "Send Page to Device">
 <!ENTITY  sendPageToDevice.accesskey         "n">
 <!ENTITY  sendLinkToDevice.label             "Send Link to Device">
 <!ENTITY  sendLinkToDevice.accesskey         "n">
+<!-- LOCALIZATION NOTE (moveTabOptions.label and moveSelectedTabOptions.label):
+These two items have the same accesskey but will never be visible at the same time. -->
+<!ENTITY  moveTabOptions.label               "Move Tab">
+<!ENTITY  moveTabOptions.accesskey           "v">
+<!ENTITY  moveSelectedTabOptions.label       "Move Tabs">
+<!ENTITY  moveSelectedTabOptions.accesskey   "v">
+<!ENTITY  moveToStart.label                  "Move to Start">
+<!ENTITY  moveToStart.accesskey              "S">
+<!ENTITY  moveToEnd.label                    "Move to End">
+<!ENTITY  moveToEnd.accesskey                "E">
 <!ENTITY  moveToNewWindow.label              "Move to New Window">
 <!ENTITY  moveToNewWindow.accesskey          "W">
 <!ENTITY  reopenInContainer.label            "Reopen in Container">
 <!ENTITY  reopenInContainer.accesskey        "e">
 <!ENTITY  bookmarkTab.label                  "Bookmark Tab">
 <!ENTITY  bookmarkTab.accesskey              "T">
 <!ENTITY  undoCloseTab.label                 "Undo Close Tab">
 <!ENTITY  undoCloseTab.accesskey             "U">