Bug 1351677 - When restoring a window, swap the initially selected tab with the desired selected tab instead of tab switching. r?mikedeboer draft
authorMike Conley <mconley@mozilla.com>
Tue, 04 Apr 2017 15:58:02 -0400
changeset 556315 a9a040ad1b14cedb52b92bf5ec752f9ec1e873e7
parent 555105 9aacfa8081b35bb8ae1a59ce3fd9d7aba57cfc7b
child 556316 2fe00dd3e308f38901c162dfce0aefd3cce48740
push id52501
push usermconley@mozilla.com
push dateWed, 05 Apr 2017 16:14:11 +0000
reviewersmikedeboer
bugs1351677
milestone55.0a1
Bug 1351677 - When restoring a window, swap the initially selected tab with the desired selected tab instead of tab switching. r?mikedeboer When restoring a window, it's cheaper if we move the initially selected tab to the index of the tab that's supposed to be selected in the restored state, rather than switching to that tab. If it turns out that moving that tab is not possible (if, for example, the user context IDs of the two tabs to swap don't match, since the userContextIds are set at tab construction time), then we fall back to tab switching. MozReview-Commit-ID: L3qP40K5DaJ
browser/components/sessionstore/SessionStore.jsm
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3199,16 +3199,19 @@ var SessionStoreInternal = {
    *        {firstWindow: true} if this is the first non-private window we're
    *                            restoring in this session, that might open an
    *                            external link as well
    */
   restoreWindow: function ssi_restoreWindow(aWindow, winData, aOptions = {}) {
     let overwriteTabs = aOptions && aOptions.overwriteTabs;
     let isFollowUp = aOptions && aOptions.isFollowUp;
     let firstWindow = aOptions && aOptions.firstWindow;
+    // See SessionStoreInternal.restoreTabs for a description of what
+    // selectTab represents.
+    let selectTab = (overwriteTabs ? parseInt(winData.selected || 1, 10) : 0);
 
     if (isFollowUp) {
       this.windowToFocus = aWindow;
     }
 
     // initialize window if necessary
     if (aWindow && (!aWindow.__SSi || !this._windows[aWindow.__SSi]))
       this.onLoad(aWindow);
@@ -3275,31 +3278,76 @@ var SessionStoreInternal = {
       // and put the newly added tab in its place.
       if (!reuseExisting && t < openTabCount) {
         tabbrowser.removeTab(tabbrowser.tabs[t]);
         tabbrowser.moveTabTo(tab, t);
       }
 
       tabs.push(tab);
 
-      if (winData.tabs[t].pinned)
-        tabbrowser.pinTab(tabs[t]);
-
       if (winData.tabs[t].hidden) {
         tabbrowser.hideTab(tabs[t]);
       } else {
         tabbrowser.showTab(tabs[t]);
         numVisibleTabs++;
       }
 
       if (!!winData.tabs[t].muted != tabs[t].linkedBrowser.audioMuted) {
         tabs[t].toggleMuteAudio(winData.tabs[t].muteReason);
       }
     }
 
+    if (selectTab > 0) {
+      // The state we're restoring wants to select a particular tab. This
+      // implies that we're overwriting tabs.
+      let currentIndex = tabbrowser.tabContainer.selectedIndex;
+      let targetIndex = selectTab - 1;
+
+      if (currentIndex != targetIndex) {
+        // We need to change the selected tab. There are two ways of doing this:
+        //
+        // 1) The fast path: swap the currently selected tab with the one in the
+        //    position of the selected tab in the restored state. Note that this
+        //    can only work if the user contexts between the two tabs being swapped
+        //    match. This should be the common case.
+        //
+        // 2) The slow path: switch to the selected tab.
+        //
+        // We'll try to do (1), and then fallback to (2).
+
+        let selectedTab = tabbrowser.selectedTab;
+        let tabAtTargetIndex = tabs[targetIndex];
+        let userContextsMatch = selectedTab.userContextId == tabAtTargetIndex.userContextId;
+
+        if (userContextsMatch) {
+          tabbrowser.moveTabTo(selectedTab, targetIndex);
+          tabbrowser.moveTabTo(tabAtTargetIndex, currentIndex);
+          // We also have to do a similar "move" in the aTabs Array to
+          // make sure that the restored content shows up in the right
+          // order.
+          tabs[targetIndex] = tabs[currentIndex];
+          tabs[currentIndex] = tabAtTargetIndex;
+        } else {
+          // Otherwise, go the slow path, and switch to the target tab.
+          tabbrowser.selectedTab = tabs[targetIndex];
+        }
+      }
+    }
+
+    for (let i = 0; i < newTabCount; ++i) {
+      if (winData.tabs[i].pinned) {
+        tabbrowser.pinTab(tabs[i]);
+      } else {
+        // Pinned tabs are clustered at the start of the tab strip. As
+        // soon as we reach a tab that isn't pinned, we know there aren't
+        // any more for this window.
+        break;
+      }
+    }
+
     if (!overwriteTabs && firstWindow) {
       // Move the originally open tabs to the end
       let endPosition = tabbrowser.tabs.length - 1;
       for (let i = 0; i < initialTabs.length; i++) {
         tabbrowser.moveTabTo(initialTabs[i], endPosition);
       }
     }
 
@@ -3366,18 +3414,17 @@ var SessionStoreInternal = {
       // ... and make sure that we don't exceed the max number of closed tabs
       // we can restore.
       this._windows[aWindow.__SSi]._closedTabs =
         newClosedTabsData.slice(0, this._max_tabs_undo);
     }
 
     // Restore tabs, if any.
     if (winData.tabs.length) {
-      this.restoreTabs(aWindow, tabs, winData.tabs,
-        (overwriteTabs ? (parseInt(winData.selected || "1")) : 0));
+      this.restoreTabs(aWindow, tabs, winData.tabs, selectTab);
     }
 
     // set smoothScroll back to the original value
     tabstrip.smoothScroll = smoothScroll;
 
     TelemetryStopwatch.finish("FX_SESSION_RESTORE_RESTORE_WINDOW_MS");
 
     this._setWindowStateReady(aWindow);
@@ -3494,20 +3541,17 @@ var SessionStoreInternal = {
     } else {
       // Remove all previous tab data except tabs that should not be overriden.
       tabsDataArray.splice(numTabsInWindow - numTabsToRestore);
     }
 
     // Let the tab data array have the right number of slots.
     tabsDataArray.length = numTabsInWindow;
 
-    // If provided, set the selected tab.
     if (aSelectTab > 0 && aSelectTab <= aTabs.length) {
-      tabbrowser.selectedTab = aTabs[aSelectTab - 1];
-
       // Update the window state in case we shut down without being notified.
       this._windows[aWindow.__SSi].selected = aSelectTab;
     }
 
     // If we restore the selected tab, make sure it goes first.
     let selectedIndex = aTabs.indexOf(tabbrowser.selectedTab);
     if (selectedIndex > -1) {
       this.restoreTab(tabbrowser.selectedTab, aTabData[selectedIndex]);