Bug 598795 - Clean up reconnect code once we have instant access to sessionstore r=dietrich, a=blocking
authorIan Gilman <ian@iangilman.com>
Wed, 05 Jan 2011 12:54:34 -0800
changeset 60031 2d849e2d302eaca2c777bbb13f96109e6f756e03
parent 60030 bddc89a2200caa8836e94704ff4084333fa28d4f
child 60032 b06a94065ef0726698fe6c42c706c8ddbe601f84
push id17837
push userian@iangilman.com
push dateWed, 05 Jan 2011 20:59:13 +0000
treeherdermozilla-central@f453924d5fe1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdietrich, blocking
bugs598795
milestone2.0b9pre
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 598795 - Clean up reconnect code once we have instant access to sessionstore r=dietrich, a=blocking
browser/base/content/tabview/tabitems.js
browser/base/content/tabview/ui.js
browser/base/content/test/tabview/browser_tabview_bug597248.js
browser/base/content/test/tabview/browser_tabview_orphaned_tabs.js
--- a/browser/base/content/tabview/tabitems.js
+++ b/browser/base/content/tabview/tabitems.js
@@ -81,16 +81,18 @@ function TabItem(tab, options) {
   this.tabCanvas = new TabCanvas(this.tab, this.canvasEl);
 
   this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
   this.locked = {};
   this.isATabItem = true;
   this._zoomPrep = false;
   this.sizeExtra = new Point();
   this.keepProportional = true;
+  this._hasBeenDrawn = false;
+  this._reconnected = false;
 
   var self = this;
 
   this.isDragging = false;
 
   this.sizeExtra.x = parseInt($div.css('padding-left'))
       + parseInt($div.css('padding-right'));
 
@@ -99,20 +101,16 @@ function TabItem(tab, options) {
 
   this.bounds = $div.bounds();
 
   this._lastTabUpdateTime = Date.now();
 
   // ___ superclass setup
   this._init($div[0]);
 
-  // ___ reconnect to data from Storage
-  this._hasBeenDrawn = false;
-  let reconnected = TabItems.reconnect(this);
-
   // ___ drag/drop
   // override dropOptions with custom tabitem methods
   // This is mostly to support the phantom groupItems.
   this.dropOptions.drop = function(e) {
     var $target = iQ(this.container);
     this.isDropTarget = false;
 
     var phantom = $target.data("phantomGroupItem");
@@ -201,29 +199,25 @@ function TabItem(tab, options) {
     .addClass('close')
     .appendTo($div);
   this.closeEl = (iQ(".close", $div))[0];
 
   iQ("<div>")
     .addClass('expander')
     .appendTo($div);
 
+  this.setResizable(true, options.immediately);
+  this.droppable(true);
   this._updateDebugBounds();
 
   TabItems.register(this);
 
-  if (!this.reconnected)
-    GroupItems.newTab(this, options);
-
-  // tabs which were not reconnected at all or were not immediately added
-  // to a group get the same treatment.
-  if (!this.reconnected || (reconnected && !reconnected.addedToGroup) ) {
-    this.setResizable(true, options.immediately);
-    this.droppable(true);
-  }
+  // ___ reconnect to data from Storage
+  if (!TabItems.reconnectingPaused())
+    this._reconnect();
 };
 
 TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
   // ----------
   // Function: forceCanvasSize
   // Repaints the thumbnail with the given resolution, and forces it
   // to stay that resolution until unforceCanvasSize is called.
   forceCanvasSize: function TabItem_forceCanvasSize(w, h) {
@@ -320,28 +314,70 @@ TabItem.prototype = Utils.extend(new Ite
   // ----------
   // Function: save
   // Store persistent for this object.
   //
   // Parameters:
   //   saveImageData - true to include thumbnail pixels (and page title as well); default false
   save: function TabItem_save(saveImageData) {
     try{
-      if (!this.tab || this.tab.parentNode == null || !this.reconnected) // too soon/late to save
+      if (!this.tab || this.tab.parentNode == null || !this._reconnected) // too soon/late to save
         return;
 
       var data = this.getStorageData(saveImageData);
       if (TabItems.storageSanity(data))
         Storage.saveTab(this.tab, data);
     } catch(e) {
       Utils.log("Error in saving tab value: "+e);
     }
   },
 
   // ----------
+  // Function: _reconnect
+  // Load the reciever's persistent data from storage. If there is none, 
+  // treats it as a new tab. 
+  _reconnect: function TabItem__reconnect() {
+    Utils.assertThrow(!this._reconnected, "shouldn't already be reconnected");
+    Utils.assertThrow(this.tab, "should have a xul:tab");
+    
+    let tabData = Storage.getTabData(this.tab);
+    if (tabData && TabItems.storageSanity(tabData)) {
+      if (this.parent)
+        this.parent.remove(this, {immediately: true});
+
+      this.setBounds(tabData.bounds, true);
+
+      if (Utils.isPoint(tabData.userSize))
+        this.userSize = new Point(tabData.userSize);
+
+      if (tabData.groupID) {
+        var groupItem = GroupItems.groupItem(tabData.groupID);
+        if (groupItem) {
+          groupItem.add(this, null, {immediately: true});
+
+          // if it matches the selected tab or no active tab and the browser 
+          // tab is hidden, the active group item would be set.
+          if (this.tab == gBrowser.selectedTab || 
+              (!GroupItems.getActiveGroupItem() && !this.tab.hidden))
+            GroupItems.setActiveGroupItem(this.parent);
+        }
+      }
+
+      if (tabData.imageData)
+        this.showCachedData(tabData);
+    } else {
+      GroupItems.newTab(this, {immediately: true});
+    }
+
+    this._reconnected = true;  
+    this.save();
+    this._sendToSubscribers("reconnected");
+  },
+  
+  // ----------
   // Function: setBounds
   // Moves this item to the specified location and size.
   //
   // Parameters:
   //   rect - a <Rect> giving the new bounds
   //   immediately - true if it should not animate; default false
   //   options - an object with additional parameters, see below
   //
@@ -747,16 +783,17 @@ let TabItems = {
   tabsProgressListener: null,
   _tabsWaitingForUpdate: [],
   _heartbeatOn: false, // see explanation at startHeartbeat() below
   _heartbeatTiming: 100, // milliseconds between _checkHeartbeat() calls
   _lastUpdateTime: Date.now(),
   _eventListeners: [],
   _pauseUpdateForTest: false,
   tempCanvas: null,
+  _reconnectingPaused: false,
 
   // ----------
   // Function: init
   // Set up the necessary tracking to maintain the <TabItems>s.
   init: function TabItems_init() {
     Utils.assert(window.AllTabs, "AllTabs must be initialized first");
     let self = this;
 
@@ -897,20 +934,16 @@ let TabItems = {
       if (iconUrl != tabItem.favImgEl.src)
         tabItem.favImgEl.src = iconUrl;
 
       // ___ URL
       let tabUrl = tab.linkedBrowser.currentURI.spec;
       if (tabUrl != tabItem.url) {
         let oldURL = tabItem.url;
         tabItem.url = tabUrl;
-
-        if (!tabItem.reconnected)
-          this.reconnect(tabItem);
-
         tabItem.save();
       }
 
       // ___ label
       let label = tab.label;
       let $name = iQ(tabItem.nameEl);
       if (!tabItem.isShowingCachedData() && $name.text() != label)
         $name.text(label);
@@ -1055,16 +1088,45 @@ let TabItems = {
   // Function: isPaintingPaused
   // Returns a boolean indicating whether painting
   // is paused or not.
   isPaintingPaused: function TabItems_isPaintingPaused() {
     return this.paintingPaused > 0;
   },
 
   // ----------
+  // Function: pauseReconnecting
+  // Don't reconnect any new tabs until resume is called.
+  pauseReconnecting: function TabItems_pauseReconnecting() {
+    Utils.assertThrow(!this._reconnectingPaused, "shouldn't already be paused");
+
+    this._reconnectingPaused = true;
+  },
+  
+  // ----------
+  // Function: resumeReconnecting
+  // Reconnect all of the tabs that were created since we paused.
+  resumeReconnecting: function TabItems_resumeReconnecting() {
+    Utils.assertThrow(this._reconnectingPaused, "should already be paused");
+
+    this._reconnectingPaused = false;
+    this.items.forEach(function(item) {
+      if (!item._reconnected)
+        item._reconnect();
+    });
+  },
+  
+  // ----------
+  // Function: reconnectingPaused
+  // Returns true if reconnecting is paused.
+  reconnectingPaused: function TabItems_reconnectingPaused() {
+    return this._reconnectingPaused;
+  },
+  
+  // ----------
   // Function: register
   // Adds the given <TabItem> to the master list.
   register: function TabItems_register(item) {
     Utils.assert(item && item.isAnItem, 'item must be a TabItem');
     Utils.assert(this.items.indexOf(item) == -1, 'only register once per item');
     this.items.push(item);
   },
 
@@ -1105,77 +1167,16 @@ let TabItems = {
   storageSanity: function TabItems_storageSanity(data) {
     var sane = true;
     if (!Utils.isRect(data.bounds)) {
       Utils.log('TabItems.storageSanity: bad bounds', data.bounds);
       sane = false;
     }
 
     return sane;
-  },
-
-  // ----------
-  // Function: reconnect
-  // Given a <TabItem>, attempts to load its persistent data from storage.
-  reconnect: function TabItems_reconnect(item) {
-    var found = false;
-
-    try{
-      Utils.assert(item, 'item');
-      Utils.assert(item.tab, 'item.tab');
-
-      if (item.reconnected)
-        return true;
-
-      if (!item.tab)
-        return false;
-
-      let tabData = Storage.getTabData(item.tab);
-      if (tabData && this.storageSanity(tabData)) {
-        if (item.parent)
-          item.parent.remove(item, {immediately: true});
-
-        item.setBounds(tabData.bounds, true);
-
-        if (Utils.isPoint(tabData.userSize))
-          item.userSize = new Point(tabData.userSize);
-
-        if (tabData.groupID) {
-          var groupItem = GroupItems.groupItem(tabData.groupID);
-          if (groupItem) {
-            groupItem.add(item, null, {immediately: true});
-
-            // if it matches the selected tab or no active tab and the browser 
-            // tab is hidden, the active group item would be set.
-            if (item.tab == gBrowser.selectedTab || 
-                (!GroupItems.getActiveGroupItem() && !item.tab.hidden))
-              GroupItems.setActiveGroupItem(item.parent);
-          }
-        }
-
-        if (tabData.imageData)
-          item.showCachedData(tabData);
-
-        item.reconnected = true;
-        found = {addedToGroup: tabData.groupID};
-      } else {
-        // We should never have any orphaned tabs. Therefore, item is not 
-        // connected if it has no parent and GroupItems.newTab() would handle 
-        // the group creation.
-        item.reconnected = (item.parent != null);
-      }
-      item.save();
-
-      if (item.reconnected)
-        item._sendToSubscribers("reconnected");
-    } catch(e) {
-      Utils.log(e);
-    }
-
-    return found;
   }
 };
 
 // ##########
 // Class: TabCanvas
 // Takes care of the actual canvas for the tab thumbnail
 // Does not need to be accessed from outside of tabitems.js
 function TabCanvas(tab, canvas) {
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -530,16 +530,17 @@ let UI = {
         self._privateBrowsing.transitionStage = 2;
       else if (self._privateBrowsing.transitionStage == 3) {
         if (self._privateBrowsing.transitionMode == "exit" &&
             self._privateBrowsing.wasInTabView)
           self.showTabView(false);
 
         self._privateBrowsing.transitionStage = 0;
         self._privateBrowsing.transitionMode = "";
+        TabItems.resumeReconnecting();
       }
     }
 
     Services.obs.addObserver(srObserver, "sessionstore-browser-state-restored", false);
 
     this._cleanupFunctions.push(function() {
       Services.obs.removeObserver(srObserver, "sessionstore-browser-state-restored");
     });
@@ -564,16 +565,17 @@ let UI = {
           self._privateBrowsing.wasInTabView = self.isTabViewVisible();
           if (self.isTabViewVisible())
             self.goToTab(gBrowser.selectedTab);
         }
       } else if (aTopic == "private-browsing-change-granted") {
         if (aData == "enter" || aData == "exit") {
           self._privateBrowsing.transitionStage = 1;
           self._privateBrowsing.transitionMode = aData;
+          TabItems.pauseReconnecting();
         }
       }
     }
 
     Services.obs.addObserver(pbObserver, "private-browsing", false);
     Services.obs.addObserver(pbObserver, "private-browsing-change-granted", false);
 
     this._cleanupFunctions.push(function() {
--- a/browser/base/content/test/tabview/browser_tabview_bug597248.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug597248.js
@@ -197,17 +197,17 @@ function onTabViewShown() {
     }
   }
 
   let tabItems = contentWindow.TabItems.getItems();
   let count = tabItems.length;
   tabItems.forEach(function(tabItem) {
     // tabitem might not be connected so use subscriber for those which are not
     // connected.
-    if (tabItem.reconnected) {
+    if (tabItem._reconnected) {
       ok(tabItem.isShowingCachedData(), 
          "Tab item is showing cached data and is already connected. " +
          tabItem.tab.linkedBrowser.currentURI.spec);
       if (--count == 0)
         nextStep();
     } else {
       tabItem.addSubscriber(tabItem, "reconnected", function() {
         tabItem.removeSubscriber(tabItem, "reconnected");
--- a/browser/base/content/test/tabview/browser_tabview_orphaned_tabs.js
+++ b/browser/base/content/test/tabview/browser_tabview_orphaned_tabs.js
@@ -87,17 +87,17 @@ function onTabViewWindowLoaded() {
       newWin.gBrowser.removeTab(tabItem.tab);
       whenWindowObservesOnce(newWin, "domwindowclosed", function() {
         finish();
       });
       newWin.close();
     };
     let tabItem = groupItem.getChild(0);
     // the item may not be connected so subscriber would be used in that case.
-    if (tabItem.reconnected) {
+    if (tabItem._reconnected) {
       checkAndFinish();
     } else {
       tabItem.addSubscriber(tabItem, "reconnected", function() {
         tabItem.removeSubscriber(tabItem, "reconnected");
         checkAndFinish();
       });
     }
   };