Bug 1442694 - Preopen pinned tabs before session restore r?dao draft
authorDoug Thayer <dothayer@mozilla.com>
Thu, 15 Mar 2018 12:38:02 -0700
changeset 823660 9d686b59fdb0b373dea342063ef936af5ada72bd
parent 823638 35a17ebc4ee64460cdac22d3fb2a57e1215e9b0f
push id117756
push userbmo:dothayer@mozilla.com
push dateFri, 27 Jul 2018 20:09:01 +0000
reviewersdao
bugs1442694
milestone63.0a1
Bug 1442694 - Preopen pinned tabs before session restore r?dao When you open firefox with pinned tabs, we first paint a window with one tab open, and then that tab gets displaced after the pinned tabs come in. MozReview-Commit-ID: GC1y6NlgLTd
browser/base/content/tabbrowser.js
browser/components/sessionstore/SessionStore.jsm
browser/modules/BrowserWindowTracker.jsm
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -32,16 +32,17 @@ window._gBrowser = {
     Services.els.addSystemEventListener(document, "keydown", this, false);
     if (AppConstants.platform == "macosx") {
       Services.els.addSystemEventListener(document, "keypress", this, false);
     }
     window.addEventListener("sizemodechange", this);
     window.addEventListener("occlusionstatechange", this);
 
     this._setupInitialBrowserAndTab();
+    this._preopenPinnedTabs();
 
     if (Services.prefs.getBoolPref("browser.display.use_system_colors")) {
       this.tabpanels.style.backgroundColor = "-moz-default-background-color";
     } else if (Services.prefs.getIntPref("browser.display.document_color_use") == 2) {
       this.tabpanels.style.backgroundColor =
         Services.prefs.getCharPref("browser.display.background_color");
     }
 
@@ -318,16 +319,39 @@ window._gBrowser = {
     let filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
                   .createInstance(Ci.nsIWebProgress);
     filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
     this._tabListeners.set(tab, tabListener);
     this._tabFilters.set(tab, filter);
     browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
   },
 
+  _preopenPinnedTabs() {
+    let numPinnedTabs = 0;
+    let windows = browserWindows();
+    windows.next();
+    let isOnlyWindow = windows.next().done;
+    if (isOnlyWindow) {
+      numPinnedTabs = Services.prefs.getIntPref("browser.tabs.firstWindowRestore.numPinnedTabs", 0);
+    }
+
+    for (let i = 0; i < numPinnedTabs; i++) {
+      let tab = this.addTab("about:blank", {
+        skipAnimation: true,
+        noInitialLabel: true,
+        skipBackgroundNotify: true,
+        createLazyBrowser: true,
+        pinned: true,
+        isForFirstWindowRestore: true,
+      });
+
+      tab.setAttribute("busy", "true");
+    }
+  },
+
   /**
    * BEGIN FORWARDED BROWSER PROPERTIES.  IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
    * MAKE SURE TO ADD IT HERE AS WELL.
    */
   get canGoBack() {
     return this.selectedBrowser.canGoBack;
   },
 
@@ -535,46 +559,63 @@ window._gBrowser = {
   },
 
   _updateTabBarForPinnedTabs() {
     this.tabContainer._unlockTabSizing();
     this.tabContainer._positionPinnedTabs();
     this.tabContainer._updateCloseButtons();
   },
 
-  _notifyPinnedStatus(aTab) {
-    this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: aTab.pinned });
+  _notifyPinnedStatus(aTab, aDeferContentMessage = false) {
+    if (!aDeferContentMessage) {
+      this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: aTab.pinned });
+    }
 
     let event = document.createEvent("Events");
     event.initEvent(aTab.pinned ? "TabPinned" : "TabUnpinned", true, false);
     aTab.dispatchEvent(event);
   },
 
+  _maybeUpdateNumPinnedTabsPref() {
+    if (BrowserWindowTracker.getTopWindow() == window) {
+      Services.prefs.setIntPref("browser.tabs.firstWindowRestore.numPinnedTabs",
+                                this._numPinnedTabs);
+    }
+  },
+
+  activatePreopenedPinnedTab(aTab) {
+    delete aTab._preopened;
+    this._insertBrowser(aTab);
+    this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: aTab.pinned });
+  },
+
   pinTab(aTab) {
     if (aTab.pinned)
       return;
 
     if (aTab.hidden)
       this.showTab(aTab);
 
     this.moveTabTo(aTab, this._numPinnedTabs);
     aTab.setAttribute("pinned", "true");
     this._updateTabBarForPinnedTabs();
     this._notifyPinnedStatus(aTab);
+    this._maybeUpdateNumPinnedTabsPref();
   },
 
   unpinTab(aTab) {
     if (!aTab.pinned)
       return;
 
     this.moveTabTo(aTab, this._numPinnedTabs - 1);
     aTab.removeAttribute("pinned");
     aTab.style.marginInlineStart = "";
     this._updateTabBarForPinnedTabs();
     this._notifyPinnedStatus(aTab);
+    this._maybeUpdateNumPinnedTabsPref();
   },
 
   previewTab(aTab, aCallback) {
     let currentTab = this.selectedTab;
     try {
       // Suppress focus, ownership and selected tab changes
       this._previewMode = true;
       this.selectedTab = aTab;
@@ -2164,16 +2205,17 @@ window._gBrowser = {
     postData,
     preferredRemoteType,
     referrerPolicy,
     referrerURI,
     relatedToCurrent,
     sameProcessAsFrameLoader,
     skipAnimation,
     skipBackgroundNotify,
+    isForFirstWindowRestore,
     triggeringPrincipal,
     userContextId,
     recordExecution,
     replayExecution,
   } = {}) {
     // if we're adding tabs, we're past interrupt mode, ditch the owner
     if (this.selectedTab.owner) {
       this.selectedTab.owner = null;
@@ -2317,16 +2359,19 @@ window._gBrowser = {
       if (tabAfter) {
         this._updateTabsAfterInsert();
       } else {
         t._tPos = index;
       }
 
       if (pinned) {
         this._updateTabBarForPinnedTabs();
+        if (!isForFirstWindowRestore) {
+          this._maybeUpdateNumPinnedTabsPref();
+        }
       }
       this.tabContainer._setPositionalAttributes();
 
       TabBarVisibility.update();
 
       // If we don't have a preferred remote type, and we have a remote
       // opener, use the opener's remote type.
       if (!preferredRemoteType && openerBrowser) {
@@ -2484,17 +2529,17 @@ window._gBrowser = {
       requestAnimationFrame(function() {
         // kick the animation off
         t.setAttribute("fadein", "true");
       });
     }
 
     // Additionally send pinned tab events
     if (pinned) {
-      this._notifyPinnedStatus(t);
+      this._notifyPinnedStatus(t, isForFirstWindowRestore);
     }
 
     return t;
   },
 
   warnAboutClosingTabs(tabsToClose, aCloseTabs, aOptionalMessage) {
     if (tabsToClose <= 1)
       return true;
@@ -2923,18 +2968,20 @@ window._gBrowser = {
     if (aTab.hidden)
       this.tabContainer._updateHiddenTabsStatus();
 
     // ... and fix up the _tPos properties immediately.
     for (let i = aTab._tPos; i < this.tabs.length; i++)
       this.tabs[i]._tPos = i;
 
     if (!this._windowIsClosing) {
-      if (wasPinned)
+      if (wasPinned) {
         this.tabContainer._positionPinnedTabs();
+        this._maybeUpdateNumPinnedTabsPref();
+      }
 
       // update tab close buttons state
       this.tabContainer._updateCloseButtons();
 
       setTimeout(function(tabs) {
         tabs._lastTabClosedByMouse = false;
       }, 0, this.tabContainer);
     }
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -3448,38 +3448,45 @@ var SessionStoreInternal = {
       for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
         if (!tabbrowser.tabs[i].selected) {
           tabbrowser.removeTab(tabbrowser.tabs[i]);
         }
       }
     }
 
     let restoreTabsLazily = this._prefBranch.getBoolPref("sessionstore.restore_tabs_lazily") && this._restore_on_demand;
+    let pinnedTabsReused = 0;
 
     for (var t = 0; t < newTabCount; t++) {
       let tabData = winData.tabs[t];
 
       let userContextId = tabData.userContextId;
       let select = t == selectTab - 1;
       let tab;
 
       // Re-use existing selected tab if possible to avoid the overhead of
       // selecting a new tab.
-      if (select &&
+      if (select && !tabData.pinned &&
           tabbrowser.selectedTab.userContextId == userContextId) {
         tab = tabbrowser.selectedTab;
-        if (!tabData.pinned) {
-          tabbrowser.unpinTab(tab);
-        }
         tabbrowser.moveTabToEnd();
         if (aWindow.gMultiProcessBrowser && !tab.linkedBrowser.isRemoteBrowser) {
           tabbrowser.updateBrowserRemoteness(tab.linkedBrowser, true);
         }
       }
 
+      if (tabData.pinned &&
+          tabbrowser.tabs[t] &&
+          tabbrowser.tabs[t].pinned &&
+          !tabbrowser.tabs[t].linkedPanel) {
+        tab = tabbrowser.tabs[t];
+        tabbrowser.activatePreopenedPinnedTab(tab);
+        pinnedTabsReused++;
+      }
+
       // Add a new tab if needed.
       if (!tab) {
         let createLazyBrowser = restoreTabsLazily && !select && !tabData.pinned;
 
         let url = "about:blank";
         if (createLazyBrowser && tabData.entries && tabData.entries.length) {
           // Let tabbrowser know the future URI because progress listeners won't
           // get onLocationChange notification before the browser is inserted.
@@ -3517,19 +3524,23 @@ var SessionStoreInternal = {
       if (tabData.pinned) {
         tabbrowser.pinTab(tab);
       }
     }
 
     // Move the originally open tabs to the end.
     if (initialTabs) {
       let endPosition = tabbrowser.tabs.length - 1;
-      for (let i = 0; i < initialTabs.length; i++) {
-        tabbrowser.unpinTab(initialTabs[i]);
-        tabbrowser.moveTabTo(initialTabs[i], endPosition);
+      for (let i = pinnedTabsReused; i < initialTabs.length; i++) {
+        if (initialTabs[i].pinned && !initialTabs[i].linkedPanel) {
+          tabbrowser.removeTab(initialTabs[i]);
+        } else {
+          tabbrowser.unpinTab(initialTabs[i]);
+          tabbrowser.moveTabTo(initialTabs[i], endPosition);
+        }
       }
     }
 
     // We want to correlate the window with data from the last session, so
     // assign another id if we have one. Otherwise clear so we don't do
     // anything with it.
     delete aWindow.__SS_lastSessionWindowID;
     if (winData.__lastSessionWindowID)
--- a/browser/modules/BrowserWindowTracker.jsm
+++ b/browser/modules/BrowserWindowTracker.jsm
@@ -87,16 +87,21 @@ function _trackWindowOrder(window) {
 }
 
 function _untrackWindowOrder(window) {
   let idx = _trackedWindows.indexOf(window);
   if (idx >= 0)
     _trackedWindows.splice(idx, 1);
 }
 
+function _trackPinnedTabs(window) {
+  Services.prefs.setIntPref("browser.tabs.firstWindowRestore.numPinnedTabs",
+                            window.gBrowser._numPinnedTabs);
+}
+
 // Methods that impact a window. Put into single object for organization.
 var WindowHelper = {
   addWindow(window) {
     // Add event listeners
     TAB_EVENTS.forEach(function(event) {
       window.gBrowser.tabContainer.addEventListener(event, _handleEvent);
     });
     WINDOW_EVENTS.forEach(function(event) {
@@ -129,16 +134,17 @@ var WindowHelper = {
 
   onActivate(window, hasFocus) {
     // If this window was the last focused window, we don't need to do anything
     if (window == _trackedWindows[0])
       return;
 
     _untrackWindowOrder(window);
     _trackWindowOrder(window);
+    _trackPinnedTabs(window);
 
     _updateCurrentContentOuterWindowID(window.gBrowser.selectedBrowser);
   },
 
   onSizemodeChange(window) {
     if (window.windowState == window.STATE_MINIMIZED) {
       // Make sure to have the minimized window at the end of the list.
       _untrackWindowOrder(window);