Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly [r=dietrich, a=blocking-2.0:final+]
☠☠ backed out by 6f4d85285727 ☠ ☠
authorPaul O’Shannessy <paul@oshannessy.com>
Fri, 03 Dec 2010 13:39:11 -0800
changeset 58571 f15120d5a96ecdcd60312a58f1955618910f6989
parent 58570 6309e9c330c0bfbc416d032ebd79984fa1917079
child 58572 28066e5483fcb4a2019299f9e45fc7ede62c2a8c
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersdietrich, blocking-2
bugs607016
milestone2.0b8pre
Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly [r=dietrich, a=blocking-2.0:final+]
browser/components/sessionstore/src/nsSessionStore.js
browser/components/sessionstore/test/browser/Makefile.in
browser/components/sessionstore/test/browser/browser_607016.js
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -1454,16 +1454,25 @@ SessionStoreService.prototype = {
       return tabData;
     else if (browser.__SS_data && browser.__SS_data._tabStillLoading) {
       // use the data to be restored when the tab hasn't been completely loaded
       tabData = browser.__SS_data;
       if (aTab.pinned)
         tabData.pinned = true;
       else
         delete tabData.pinned;
+      tabData.hidden = aTab.hidden;
+
+      // If __SS_extdata is set then we'll use that since it might be newer.
+      if (aTab.__SS_extdata)
+        tabData.extData = aTab.__SS_extdata;
+      // If it exists but is empty then a key was likely deleted. In that case just
+      // delete extData.
+      if (tabData.extData && !Object.keys(tabData.extData).length)
+        delete tabData.extData;
       return tabData;
     }
     
     var history = null;
     try {
       history = browser.sessionHistory;
     }
     catch (ex) { } // this could happen if we catch a tab during (de)initialization
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -118,16 +118,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_579879.js \
 	browser_580512.js \
 	browser_581593.js \
 	browser_586147.js \
 	browser_586068-cascaded_restore.js \
 	browser_589246.js \
 	browser_590268.js \
 	browser_600545.js \
+	browser_607016.js \
 	$(NULL)
 
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	$(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_607016.js
@@ -0,0 +1,151 @@
+/* ***** 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 sessionstore test code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Paul O’Shannessy <paul@oshannessy.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 ***** */
+
+const TAB_STATE_NEEDS_RESTORE = 1;
+const TAB_STATE_RESTORING = 2;
+
+Cu.import("resource://gre/modules/Services.jsm");
+let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+         getService(Ci.nsISessionStore);
+
+let stateBackup = ss.getBrowserState();
+
+function cleanup() {
+  // Reset the pref
+  try {
+    Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
+  } catch (e) {}
+  ss.setBrowserState(stateBackup);
+  executeSoon(finish);
+}
+
+function test() {
+  /** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/
+  waitForExplicitFinish();
+
+  // Set the pref to 0 so we know exactly how many tabs should be restoring at
+  // any given time. This guarantees that a finishing load won't start another.
+  Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0);
+
+  // We have our own progress listener for this test, which we'll attach before our state is set
+  let progressListener = {
+    onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+      if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
+          aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
+        progressCallback(aBrowser);
+    }
+  }
+
+  let state = { windows: [{ tabs: [
+    { entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
+    { entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } }, // overwriting
+    { entries: [{ url: "http://example.org#3" }], extData: { "uniq": r() } }, // hiding
+    { entries: [{ url: "http://example.org#4" }], extData: { "uniq": r() } }, // adding
+    { entries: [{ url: "http://example.org#5" }], extData: { "uniq": r() } }, // deleting
+    { entries: [{ url: "http://example.org#6" }] } // creating
+  ], selected: 1 }] };
+
+  function progressCallback(aBrowser) {
+    // We'll remove the progress listener after the first one because we aren't
+    // loading any other tabs
+    window.gBrowser.removeTabsProgressListener(progressListener);
+
+    let curState = JSON.parse(ss.getBrowserState());
+    for (let i = 0; i < curState.windows[0].tabs.length; i++) {
+      if (state.windows[0].tabs[i].extData) {
+        is(curState.windows[0].tabs[i].extData["uniq"],
+           state.windows[0].tabs[i].extData["uniq"],
+           "sanity check that tab has correct extData");
+      }
+      else
+        ok(!("extData" in curState.windows[0].tabs[i]),
+           "sanity check that tab doesn't have extData");
+    }
+
+    // Now we'll set a new unique value on 1 of the tabs
+    let newUniq = r();
+    ss.setTabValue(gBrowser.tabs[1], "uniq", newUniq);
+    gBrowser.removeTab(gBrowser.tabs[1]);
+    let closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+    is(closedTabData.state.extData.uniq, newUniq,
+       "(overwriting) new data is stored in extData");
+
+    // hide the next tab before closing it
+    gBrowser.hideTab(gBrowser.tabs[1]);
+    gBrowser.removeTab(gBrowser.tabs[1]);
+    closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+    ok(closedTabData.state.hidden, "(hiding) tab data has hidden == true");
+
+    // set data that's not in a conflicting key
+    let stillUniq = r();
+    ss.setTabValue(gBrowser.tabs[1], "stillUniq", stillUniq);
+    gBrowser.removeTab(gBrowser.tabs[1]);
+    closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+    is(closedTabData.state.extData.stillUniq, stillUniq,
+       "(adding) new data is stored in extData");
+
+    // remove the uniq value and make sure it's not there in the closed data
+    ss.deleteTabValue(gBrowser.tabs[1], "uniq");
+    gBrowser.removeTab(gBrowser.tabs[1]);
+    closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+    ok(!("extData" in closedTabData.state),
+       "(deleting) no data is stored in extData");
+    if ("extData" in closedTabData.state)
+      info("extData: " + JSON.stringify(closedTabData.state.extData));
+
+    // set unique data on the tab that never had any set, make sure that's saved
+    let newUniq2 = r();
+    ss.setTabValue(gBrowser.tabs[1], "uniq", newUniq2);
+    gBrowser.removeTab(gBrowser.tabs[1]);
+    closedTabData = (JSON.parse(ss.getClosedTabData(window)))[0];
+    is(closedTabData.state.extData.uniq, newUniq2,
+       "(creating) new data is stored in extData where there was none");
+
+    cleanup();
+  }
+
+  window.gBrowser.addTabsProgressListener(progressListener);
+  ss.setBrowserState(JSON.stringify(state));
+}
+
+// Helper function to create a random value
+function r() {
+  return "" + Date.now() + Math.random();
+}
+