--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -4,19 +4,23 @@
"use strict";
var EXPORTED_SYMBOLS = ["SessionStore"];
// Current version of the format used by Session Restore.
const FORMAT_VERSION = 1;
+const TAB_CUSTOM_VALUES = new WeakMap();
+const TAB_LAZY_STATES = new WeakMap();
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
const TAB_STATE_WILL_RESTORE = 3;
+const TAB_STATE_FOR_BROWSER = new WeakMap();
+const WINDOW_RESTORE_IDS = new WeakMap();
// A new window has just been restored. At this stage, tabs are generally
// not restored.
const NOTIFY_SINGLE_WINDOW_RESTORED = "sessionstore-single-window-restored";
const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored";
const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored";
const NOTIFY_LAST_SESSION_CLEARED = "sessionstore-last-session-cleared";
const NOTIFY_RESTORING_ON_STARTUP = "sessionstore-restoring-on-startup";
@@ -248,16 +252,20 @@ var SessionStore = {
getTabState: function ss_getTabState(aTab) {
return SessionStoreInternal.getTabState(aTab);
},
setTabState: function ss_setTabState(aTab, aState) {
SessionStoreInternal.setTabState(aTab, aState);
},
+ getInternalObjectState(obj) {
+ return SessionStoreInternal.getInternalObjectState(obj);
+ },
+
duplicateTab: function ss_duplicateTab(aWindow, aTab, aDelta = 0, aRestoreImmediately = true) {
return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta, aRestoreImmediately);
},
getClosedTabCount: function ss_getClosedTabCount(aWindow) {
return SessionStoreInternal.getClosedTabCount(aWindow);
},
@@ -910,17 +918,17 @@ var SessionStoreInternal = {
// longer consider its data interesting enough to keep around.
this.removeClosedTabData(closedTabs, index);
}
}
}
break;
case "SessionStore:restoreHistoryComplete": {
// Notify the tabbrowser that the tab chrome has been restored.
- let tabData = TabState.collect(tab);
+ let tabData = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
// wall-paper fix for bug 439675: make sure that the URL to be loaded
// is always visible in the address bar if no other value is present
let activePageData = tabData.entries[tabData.index - 1] || null;
let uri = activePageData ? activePageData.url || null : null;
// NB: we won't set initial URIs (about:home, about:newtab, etc.) here
// because their load will not normally trigger a location bar clearing
// when they finish loading (to avoid race conditions where we then
@@ -938,30 +946,30 @@ var SessionStoreInternal = {
this.updateTabLabelAndIcon(tab, tabData);
let event = win.document.createEvent("Events");
event.initEvent("SSTabRestoring", true, false);
tab.dispatchEvent(event);
break;
}
case "SessionStore:restoreTabContentStarted":
- if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+ if (TAB_STATE_FOR_BROWSER.get(browser) == TAB_STATE_NEEDS_RESTORE) {
// If a load not initiated by sessionstore was started in a
// previously pending tab. Mark the tab as no longer pending.
this.markTabAsRestoring(tab);
} else if (data.reason != RESTORE_TAB_CONTENT_REASON.NAVIGATE_AND_RESTORE) {
// If the user was typing into the URL bar when we crashed, but hadn't hit
// enter yet, then we just need to write that value to the URL bar without
// loading anything. This must happen after the load, as the load will clear
// userTypedValue.
//
// Note that we only want to do that if we're restoring state for reasons
// _other_ than a navigateAndRestore remoteness-flip, as such a flip
// implies that the user was navigating.
- let tabData = TabState.collect(tab);
+ let tabData = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
if (tabData.userTypedValue && !tabData.userTypedClear && !browser.userTypedValue) {
browser.userTypedValue = tabData.userTypedValue;
win.URLBarSetURI();
}
// Remove state we don't need any longer.
TabStateCache.update(browser, {
userTypedValue: null, userTypedClear: null
@@ -1179,17 +1187,17 @@ var SessionStoreInternal = {
} else {
// Nothing to restore, notify observers things are complete.
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
Services.obs.notifyObservers(null, "sessionstore-one-or-no-tab-restored");
this._deferredAllWindowsRestored.resolve();
}
// this window was opened by _openWindowWithState
} else if (!this._isWindowLoaded(aWindow)) {
- let state = this._statesToRestore[aWindow.__SS_restoreID];
+ let state = this._statesToRestore[WINDOW_RESTORE_IDS.get(aWindow)];
let options = {overwriteTabs: true, isFollowUp: state.windows.length == 1};
this.restoreWindow(aWindow, state.windows[0], options);
// The user opened another, non-private window after starting up with
// a single private one. Let's restore the session we actually wanted to
// restore at startup.
} else if (this._deferredInitialState && !isPrivateWindow &&
aWindow.toolbar.visible) {
@@ -1347,19 +1355,20 @@ var SessionStoreInternal = {
let completionPromise = Promise.resolve();
// this window was about to be restored - conserve its original data, if any
let isFullyLoaded = this._isWindowLoaded(aWindow);
if (!isFullyLoaded) {
if (!aWindow.__SSi) {
aWindow.__SSi = this._generateWindowID();
}
- this._windows[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID];
- delete this._statesToRestore[aWindow.__SS_restoreID];
- delete aWindow.__SS_restoreID;
+ let restoreID = WINDOW_RESTORE_IDS.get(aWindow);
+ this._windows[aWindow.__SSi] = this._statesToRestore[restoreID];
+ delete this._statesToRestore[restoreID];
+ WINDOW_RESTORE_IDS.delete(aWindow);
}
// ignore windows not tracked by SessionStore
if (!aWindow.__SSi || !this._windows[aWindow.__SSi]) {
return completionPromise;
}
// notify that the session store will stop tracking this window so that
@@ -1893,23 +1902,23 @@ var SessionStoreInternal = {
browser.addEventListener("SwapDocShells", this);
browser.addEventListener("oop-browser-crashed", this);
if (browser.frameLoader) {
this._lastKnownFrameLoader.set(browser.permanentKey, browser.frameLoader);
}
// Only restore if browser has been lazy.
- if (aTab.__SS_lazyData && !browser.__SS_restoreState && TabStateCache.get(browser)) {
- let tabState = TabState.clone(aTab);
+ if (TAB_LAZY_STATES.has(aTab) && !TAB_STATE_FOR_BROWSER.has(browser) && TabStateCache.get(browser)) {
+ let tabState = TabState.clone(aTab, TAB_CUSTOM_VALUES.get(aTab));
this.restoreTab(aTab, tabState);
}
// The browser has been inserted now, so lazy data is no longer relevant.
- delete aTab.__SS_lazyData;
+ TAB_LAZY_STATES.delete(aTab);
},
/**
* remove listeners for a tab
* @param aWindow
* Window reference
* @param aTab
* Tab reference
@@ -1939,17 +1948,17 @@ var SessionStoreInternal = {
aTab.dispatchEvent(event);
// don't update our internal state if we don't have to
if (this._max_tabs_undo == 0) {
return;
}
// Get the latest data for this tab (generally, from the cache)
- let tabState = TabState.collect(aTab);
+ let tabState = TabState.collect(aTab, TAB_CUSTOM_VALUES.get(aTab));
// Don't save private tabs
let isPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(aWindow);
if (!isPrivateWindow && tabState.isPrivate) {
return;
}
// Store closed-tab data for undo.
@@ -2002,39 +2011,39 @@ var SessionStoreInternal = {
this.cleanUpRemovedBrowser(aTab);
aTab.setAttribute("pending", "true");
this._lastKnownFrameLoader.delete(browser.permanentKey);
this._crashedBrowsers.delete(browser.permanentKey);
aTab.removeAttribute("crashed");
- aTab.__SS_lazyData = {
+ TAB_LAZY_STATES.set(aTab, {
url: browser.currentURI.spec,
title: aTab.label,
userTypedValue: browser.userTypedValue || "",
userTypedClear: browser.userTypedClear || 0
- };
+ });
},
/**
* When a tab is removed or suspended, remove listeners and reset restoring state.
* @param aBrowser
* Browser reference
*/
cleanUpRemovedBrowser(aTab) {
let browser = aTab.linkedBrowser;
browser.removeEventListener("SwapDocShells", this);
browser.removeEventListener("oop-browser-crashed", this);
// If this tab was in the middle of restoring or still needs to be restored,
// we need to reset that state. If the tab was restoring, we will attempt to
// restore the next tab.
- let previousState = browser.__SS_restoreState;
+ let previousState = TAB_STATE_FOR_BROWSER.get(browser);
if (previousState) {
this._resetTabRestoringState(aTab);
if (previousState == TAB_STATE_RESTORING)
this.restoreNextTab();
}
},
/**
@@ -2109,18 +2118,18 @@ var SessionStoreInternal = {
*/
onTabSelect: function ssi_onTabSelect(aWindow) {
if (RunState.isRunning) {
this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex;
let tab = aWindow.gBrowser.selectedTab;
let browser = tab.linkedBrowser;
- if (browser.__SS_restoreState &&
- browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+ if (TAB_STATE_FOR_BROWSER.get(browser) == TAB_STATE_NEEDS_RESTORE) {
+ // If BROWSER_STATE is still available for the browser and it is
// If __SS_restoreState is still on the browser and it is
// TAB_STATE_NEEDS_RESTORE, then then we haven't restored
// this tab yet.
//
// It's possible that this tab was recently revived, and that
// we've deferred showing the tab crashed page for it (if the
// tab crashed in the background). If so, we need to re-enter
// the crashed state, since we'll be showing the tab crashed
@@ -2131,35 +2140,33 @@ var SessionStoreInternal = {
this.restoreTabContent(tab);
}
}
}
},
onTabShow: function ssi_onTabShow(aWindow, aTab) {
// If the tab hasn't been restored yet, move it into the right bucket
- if (aTab.linkedBrowser.__SS_restoreState &&
- aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+ if (TAB_STATE_FOR_BROWSER.get(aTab.linkedBrowser) == TAB_STATE_NEEDS_RESTORE) {
TabRestoreQueue.hiddenToVisible(aTab);
// let's kick off tab restoration again to ensure this tab gets restored
// with "restore_hidden_tabs" == false (now that it has become visible)
this.restoreNextTab();
}
// Default delay of 2 seconds gives enough time to catch multiple TabShow
// events. This used to be due to changing groups in 'tab groups'. We
// might be able to get rid of this now?
this.saveStateDelayed(aWindow);
},
onTabHide: function ssi_onTabHide(aWindow, aTab) {
// If the tab hasn't been restored yet, move it into the right bucket
- if (aTab.linkedBrowser.__SS_restoreState &&
- aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+ if (TAB_STATE_FOR_BROWSER.get(aTab.linkedBrowser) == TAB_STATE_NEEDS_RESTORE) {
TabRestoreQueue.visibleToHidden(aTab);
}
// Default delay of 2 seconds gives enough time to catch multiple TabHide
// events. This used to be due to changing groups in 'tab groups'. We
// might be able to get rid of this now?
this.saveStateDelayed(aWindow);
},
@@ -2191,17 +2198,17 @@ var SessionStoreInternal = {
this._crashedBrowsers.add(browser.permanentKey);
let win = browser.ownerGlobal;
// If we hadn't yet restored, or were still in the midst of
// restoring this browser at the time of the crash, we need
// to reset its state so that we can try to restore it again
// when the user revives the tab from the crash.
- if (browser.__SS_restoreState) {
+ if (TAB_STATE_FOR_BROWSER.has(browser)) {
let tab = win.gBrowser.getTabForBrowser(browser);
this._resetLocalTabRestoringState(tab);
}
},
// Clean up data that has been closed a long time ago.
// Do not reschedule a save. This will wait for the next regular
// save.
@@ -2335,17 +2342,17 @@ var SessionStoreInternal = {
getTabState: function ssi_getTabState(aTab) {
if (!aTab || !aTab.ownerGlobal) {
throw Components.Exception("Need a valid tab", Cr.NS_ERROR_INVALID_ARG);
}
if (!aTab.ownerGlobal.__SSi) {
throw Components.Exception("Default view is not tracked", Cr.NS_ERROR_INVALID_ARG);
}
- let tabState = TabState.collect(aTab);
+ let tabState = TabState.collect(aTab, TAB_CUSTOM_VALUES.get(aTab));
return JSON.stringify(tabState);
},
setTabState(aTab, aState) {
// Remove the tab state from the cache.
// Note that we cannot simply replace the contents of the cache
// as |aState| can be an incomplete state that will be completed
@@ -2361,26 +2368,33 @@ var SessionStoreInternal = {
throw Components.Exception("Invalid state object: no entries", Cr.NS_ERROR_INVALID_ARG);
}
let window = aTab.ownerGlobal;
if (!window || !("__SSi" in window)) {
throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
}
- if (aTab.linkedBrowser.__SS_restoreState) {
+ if (TAB_STATE_FOR_BROWSER.has(aTab.linkedBrowser)) {
this._resetTabRestoringState(aTab);
}
this.restoreTab(aTab, tabState);
// Notify of changes to closed objects.
this._notifyOfClosedObjectsChange();
},
+ getInternalObjectState(obj) {
+ if (obj.__SSi) {
+ return this._windows[obj.__SSi];
+ }
+ return obj.loadURI ? TAB_STATE_FOR_BROWSER.get(obj) : TAB_CUSTOM_VALUES.get(obj);
+ },
+
duplicateTab: function ssi_duplicateTab(aWindow, aTab, aDelta = 0, aRestoreImmediately = true) {
if (!aTab || !aTab.ownerGlobal) {
throw Components.Exception("Need a valid tab", Cr.NS_ERROR_INVALID_ARG);
}
if (!aTab.ownerGlobal.__SSi) {
throw Components.Exception("Default view is not tracked", Cr.NS_ERROR_INVALID_ARG);
}
if (!aWindow.gBrowser) {
@@ -2393,17 +2407,17 @@ var SessionStoreInternal = {
aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab, userContextId}) :
aWindow.gBrowser.addTab(null, {userContextId});
// Start the throbber to pretend we're doing something while actually
// waiting for data from the frame script.
newTab.setAttribute("busy", "true");
// Collect state before flushing.
- let tabState = TabState.clone(aTab);
+ let tabState = TabState.collect(aTab, TAB_CUSTOM_VALUES.get(aTab));
// Flush to get the latest tab state to duplicate.
let browser = aTab.linkedBrowser;
TabStateFlusher.flush(browser).then(() => {
// The new tab might have been closed in the meantime.
if (newTab.closing || !newTab.linkedBrowser) {
return;
}
@@ -2590,52 +2604,53 @@ var SessionStoreInternal = {
deleteWindowValue: function ssi_deleteWindowValue(aWindow, aKey) {
if (aWindow.__SSi && this._windows[aWindow.__SSi].extData &&
this._windows[aWindow.__SSi].extData[aKey])
delete this._windows[aWindow.__SSi].extData[aKey];
this.saveStateDelayed(aWindow);
},
getCustomTabValue(aTab, aKey) {
- return (aTab.__SS_extdata || {})[aKey] || "";
+ return (TAB_CUSTOM_VALUES.get(aTab) || {})[aKey] || "";
},
setCustomTabValue(aTab, aKey, aStringValue) {
if (typeof aStringValue != "string") {
throw new TypeError("setCustomTabValue only accepts string values");
}
// If the tab hasn't been restored, then set the data there, otherwise we
// could lose newly added data.
- if (!aTab.__SS_extdata) {
- aTab.__SS_extdata = {};
- }
-
- aTab.__SS_extdata[aKey] = aStringValue;
+ if (!TAB_CUSTOM_VALUES.has(aTab)) {
+ TAB_CUSTOM_VALUES.set(aTab, {});
+ }
+
+ TAB_CUSTOM_VALUES.get(aTab)[aKey] = aStringValue;
this.saveStateDelayed(aTab.ownerGlobal);
},
deleteCustomTabValue(aTab, aKey) {
- if (aTab.__SS_extdata && aKey in aTab.__SS_extdata) {
- delete aTab.__SS_extdata[aKey];
+ let state = TAB_CUSTOM_VALUES.get(aTab);
+ if (state && aKey in state) {
+ delete state[aKey];
this.saveStateDelayed(aTab.ownerGlobal);
}
},
/**
* Retrieves data specific to lazy-browser tabs. If tab is not lazy,
* will return undefined.
*
* @param aTab (xul:tab)
* The tabbrowser-tab the data is for.
* @param aKey (string)
* The key which maps to the desired data.
*/
getLazyTabValue(aTab, aKey) {
- return (aTab.__SS_lazyData || {})[aKey];
+ return (TAB_LAZY_STATES.get(aTab) || {})[aKey];
},
getGlobalValue: function ssi_getGlobalValue(aKey) {
return this._globalState.get(aKey);
},
setGlobalValue: function ssi_setGlobalValue(aKey, aStringValue) {
if (typeof aStringValue != "string") {
@@ -2707,17 +2722,17 @@ var SessionStoreInternal = {
if (tab.hasAttribute("customizemode")) {
return;
}
let browser = tab.linkedBrowser;
let win = browser.ownerGlobal;
if (!tabData) {
- tabData = TabState.collect(tab);
+ tabData = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
if (!tabData) {
throw new Error("tabData not found for given tab");
}
}
let activePageData = tabData.entries[tabData.index - 1] || null;
// If the page has a title, set it.
@@ -2903,17 +2918,17 @@ var SessionStoreInternal = {
// We put the browser at about:blank in case the user is
// restoring tabs on demand. This way, the user won't see
// a flash of the about:tabcrashed page after selecting
// the revived tab.
aTab.removeAttribute("crashed");
browser.loadURI("about:blank");
- let data = TabState.collect(aTab);
+ let data = TabState.collect(aTab, TAB_CUSTOM_VALUES.get(aTab));
this.restoreTab(aTab, data, {
forceOnDemand: true,
});
},
/**
* Revive all crashed tabs and reset the crashed tabs count to 0.
*/
@@ -2984,17 +2999,17 @@ var SessionStoreInternal = {
let refreshedWindow = tab.ownerGlobal;
// The tab or its window might be gone.
if (!refreshedWindow || !refreshedWindow.__SSi || refreshedWindow.closed) {
return;
}
- let tabState = TabState.clone(tab);
+ let tabState = TabState.clone(tab, TAB_CUSTOM_VALUES.get(tab));
let options = {
restoreImmediately: true,
// We want to make sure that this information is passed to restoreTab
// whether or not a historyIndex is passed in. Thus, we extract it from
// the loadArguments.
newFrameloader: recentLoadArguments.newFrameloader,
remoteType: recentLoadArguments.remoteType,
// Make sure that SessionStore knows that this restoration is due
@@ -3005,25 +3020,25 @@ var SessionStoreInternal = {
if (historyIndex >= 0) {
tabState.index = historyIndex + 1;
tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
} else {
options.loadArguments = recentLoadArguments;
}
// Need to reset restoring tabs.
- if (tab.linkedBrowser.__SS_restoreState) {
+ if (TAB_STATE_FOR_BROWSER.has(tab.linkedBrowser)) {
this._resetLocalTabRestoringState(tab);
}
// Restore the state into the tab.
this.restoreTab(tab, tabState, options);
});
- tab.linkedBrowser.__SS_restoreState = TAB_STATE_WILL_RESTORE;
+ TAB_STATE_FOR_BROWSER.set(tab.linkedBrowser, TAB_STATE_WILL_RESTORE);
// Notify of changes to closed objects.
this._notifyOfClosedObjectsChange();
},
/**
* Retrieves the latest session history information for a tab. The cached data
* is returned immediately, but a callback may be provided that supplies
@@ -3042,17 +3057,17 @@ var SessionStoreInternal = {
if (sessionHistory) {
updatedCallback(sessionHistory);
}
});
}
// Don't continue if the tab was closed before TabStateFlusher.flush resolves.
if (tab.linkedBrowser) {
- let tabState = TabState.collect(tab);
+ let tabState = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
return { index: tabState.index - 1, entries: tabState.entries };
}
return null;
},
/**
* See if aWindow is usable for use when restoring a previous session via
* restoreLastSession. If usable, prepare it for use.
@@ -3254,17 +3269,17 @@ var SessionStoreInternal = {
/**
* serialize session data for a window
* @param aWindow
* Window reference
* @returns string
*/
_getWindowState: function ssi_getWindowState(aWindow) {
if (!this._isWindowLoaded(aWindow))
- return this._statesToRestore[aWindow.__SS_restoreID];
+ return this._statesToRestore[WINDOW_RESTORE_IDS.get(aWindow)];
if (RunState.isRunning) {
this._collectWindowData(aWindow);
}
return { windows: [this._windows[aWindow.__SSi]] };
},
@@ -3285,17 +3300,17 @@ var SessionStoreInternal = {
let tabbrowser = aWindow.gBrowser;
let tabs = tabbrowser.tabs;
let winData = this._windows[aWindow.__SSi];
let tabsData = winData.tabs = [];
// update the internal state data for this window
for (let tab of tabs) {
- let tabData = TabState.collect(tab);
+ let tabData = TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
tabMap.set(tab, tabData);
tabsData.push(tabData);
}
winData.selected = tabbrowser.tabbox.selectedIndex + 1;
this._updateWindowFeatures(aWindow);
// Make sure we keep __SS_lastSessionWindowID around for cases like entering
@@ -3544,27 +3559,28 @@ var SessionStoreInternal = {
* Make a connection to a host when users hover mouse on a tab.
* This will also set a flag in the tab to prevent us from speculatively
* connecting a second time.
*
* @param tab
* a tab to speculatively connect on mouse hover.
*/
speculativeConnectOnTabHover(tab) {
- if (tab.__SS_lazyData && !tab.__SS_connectionPrepared) {
+ let tabState = TAB_LAZY_STATES.get(tab);
+ if (tabState && !tabState.connectionPrepared) {
let url = this.getLazyTabValue(tab, "url");
let prepared = this.prepareConnectionToHost(url);
// This is used to test if a connection has been made beforehand.
if (gDebuggingEnabled) {
tab.__test_connection_prepared = prepared;
tab.__test_connection_url = url;
}
// A flag indicate that we've prepared a connection for this tab and
// if is called again, we shouldn't prepare another connection.
- tab.__SS_connectionPrepared = true;
+ tabState.connectionPrepared = true;
}
},
/**
* Restore multiple windows using the provided state.
* @param aWindow
* Window reference to the first window to use for restoration.
* Additionally required windows will be opened.
@@ -3644,18 +3660,18 @@ var SessionStoreInternal = {
* indicates the first tab should be selected, and "0" indicates that
* the currently selected tab will not be changed.
*/
restoreTabs(aWindow, aTabs, aTabData, aSelectTab) {
var tabbrowser = aWindow.gBrowser;
if (!this._isWindowLoaded(aWindow)) {
// from now on, the data will come from the actual window
- delete this._statesToRestore[aWindow.__SS_restoreID];
- delete aWindow.__SS_restoreID;
+ delete this._statesToRestore[WINDOW_RESTORE_IDS.get(aWindow)];
+ WINDOW_RESTORE_IDS.delete(aWindow);
delete this._windows[aWindow.__SSi]._restoring;
}
let numTabsToRestore = aTabs.length;
let numTabsInWindow = tabbrowser.tabs.length;
let tabsDataArray = this._windows[aWindow.__SSi].tabs;
// Update the window state in case we shut down without being notified.
@@ -3689,17 +3705,17 @@ var SessionStoreInternal = {
}
}
},
// Restores the given tab state for a given tab.
restoreTab(tab, tabData, options = {}) {
let browser = tab.linkedBrowser;
- if (browser.__SS_restoreState) {
+ if (TAB_STATE_FOR_BROWSER.has(browser)) {
Cu.reportError("Must reset tab before calling restoreTab.");
return;
}
let loadArguments = options.loadArguments;
let window = tab.ownerGlobal;
let tabbrowser = window.gBrowser;
let forceOnDemand = options.forceOnDemand;
@@ -3758,19 +3774,19 @@ var SessionStoreInternal = {
// Ensure that we persist tab attributes restored from previous sessions.
Object.keys(tabData.attributes).forEach(a => TabAttributes.persist(a));
}
if (!tabData.entries) {
tabData.entries = [];
}
if (tabData.extData) {
- tab.__SS_extdata = Cu.cloneInto(tabData.extData, {});
+ TAB_CUSTOM_VALUES.set(tab, Cu.cloneInto(tabData.extData, {}));
} else {
- delete tab.__SS_extdata;
+ TAB_CUSTOM_VALUES.delete(tab);
}
// Tab is now open.
delete tabData.closedAt;
// Ensure the index is in bounds.
let activeIndex = (tabData.index || tabData.entries.length) - 1;
activeIndex = Math.min(activeIndex, tabData.entries.length - 1);
@@ -3814,17 +3830,17 @@ var SessionStoreInternal = {
// Start a new epoch to discard all frame script messages relating to a
// previous epoch. All async messages that are still on their way to chrome
// will be ignored and don't override any tab data set when restoring.
let epoch = this.startNextEpoch(browser);
// Ensure that the tab will get properly restored in the event the tab
// crashes while restoring. But don't set this on lazy browsers as
// restoreTab will get called again when the browser is instantiated.
- browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
+ TAB_STATE_FOR_BROWSER.set(browser, TAB_STATE_NEEDS_RESTORE);
this._sendRestoreHistory(browser, {tabData, epoch, loadArguments});
// This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but
// it ensures each window will have its selected tab loaded.
if (willRestoreImmediately) {
this.restoreTabContent(tab, options);
} else if (!forceOnDemand) {
@@ -3840,33 +3856,33 @@ var SessionStoreInternal = {
tab.__test_connection_prepared = prepared;
tab.__test_connection_url = url;
}
}
}
this.restoreNextTab();
}
} else {
- // __SS_lazyData holds data for lazy-browser tabs to proxy for
+ // TAB_LAZY_STATES holds data for lazy-browser tabs to proxy for
// data unobtainable from the unbound browser. This only applies to lazy
// browsers and will be removed once the browser is inserted in the document.
// This must preceed `updateTabLabelAndIcon` call for required data to be present.
let url = "about:blank";
let title = "";
if (activeIndex in tabData.entries) {
url = tabData.entries[activeIndex].url;
title = tabData.entries[activeIndex].title || url;
}
- tab.__SS_lazyData = {
+ TAB_LAZY_STATES.set(tab, {
url,
title,
userTypedValue: tabData.userTypedValue || "",
userTypedClear: tabData.userTypedClear || 0
- };
+ });
}
if (tab.hasAttribute("customizemode")) {
window.gCustomizeMode.setTab(tab);
}
// Update tab label and icon to show something
// while we wait for the messages to be processed.
@@ -3888,17 +3904,17 @@ var SessionStoreInternal = {
let loadArguments = aOptions.loadArguments;
if (aTab.hasAttribute("customizemode") && !loadArguments) {
return;
}
let browser = aTab.linkedBrowser;
let window = aTab.ownerGlobal;
let tabbrowser = window.gBrowser;
- let tabData = TabState.clone(aTab);
+ let tabData = TabState.clone(aTab, TAB_CUSTOM_VALUES.get(aTab));
let activeIndex = tabData.index - 1;
let activePageData = tabData.entries[activeIndex] || null;
let uri = activePageData ? activePageData.url || null : null;
if (loadArguments) {
uri = loadArguments.uri;
if (loadArguments.userContextId) {
browser.setAttribute("usercontextid", loadArguments.userContextId);
}
@@ -3954,28 +3970,28 @@ var SessionStoreInternal = {
/**
* Marks a given pending tab as restoring.
*
* @param aTab
* the pending tab to mark as restoring
*/
markTabAsRestoring(aTab) {
let browser = aTab.linkedBrowser;
- if (browser.__SS_restoreState != TAB_STATE_NEEDS_RESTORE) {
+ if (TAB_STATE_FOR_BROWSER.get(browser) != TAB_STATE_NEEDS_RESTORE) {
throw new Error("Given tab is not pending.");
}
// Make sure that this tab is removed from the priority queue.
TabRestoreQueue.remove(aTab);
// Increase our internal count.
this._tabsRestoringCount++;
// Set this tab's state to restoring
- browser.__SS_restoreState = TAB_STATE_RESTORING;
+ TAB_STATE_FOR_BROWSER.set(browser, TAB_STATE_RESTORING);
aTab.removeAttribute("pending");
},
/**
* This _attempts_ to restore the next available tab. If the restore fails,
* then we will attempt the next one.
* There are conditions where this won't do anything:
* if we're in the process of quitting
@@ -4277,17 +4293,18 @@ var SessionStoreInternal = {
var window =
Services.ww.openWindow(null, this._prefBranch.getCharPref("chromeURL"),
"_blank", features, argString);
do {
var ID = "window" + Math.random();
} while (ID in this._statesToRestore);
- this._statesToRestore[(window.__SS_restoreID = ID)] = aState;
+ WINDOW_RESTORE_IDS.set(window, ID);
+ this._statesToRestore[ID] = aState;
return window;
},
/**
* Whether or not to resume session, if not recovering from a crash.
* @returns bool
*/
@@ -4602,17 +4619,17 @@ var SessionStoreInternal = {
_setWindowStateBusyValue:
function ssi_changeWindowStateBusyValue(aWindow, aValue) {
this._windows[aWindow.__SSi].busy = aValue;
// Keep the to-be-restored state in sync because that is returned by
// getWindowState() as long as the window isn't loaded, yet.
if (!this._isWindowLoaded(aWindow)) {
- let stateToRestore = this._statesToRestore[aWindow.__SS_restoreID].windows[0];
+ let stateToRestore = this._statesToRestore[WINDOW_RESTORE_IDS.get(aWindow)].windows[0];
stateToRestore.busy = aValue;
}
},
/**
* Set the given window's state to 'not busy'.
* @param aWindow the window
*/
@@ -4683,17 +4700,17 @@ var SessionStoreInternal = {
/**
* @param aWindow
* Window reference
* @returns whether this window's data is still cached in _statesToRestore
* because it's not fully loaded yet
*/
_isWindowLoaded: function ssi_isWindowLoaded(aWindow) {
- return !aWindow.__SS_restoreID;
+ return !WINDOW_RESTORE_IDS.has(aWindow);
},
/**
* Resize this._closedWindows to the value of the pref, except in the case
* where we don't have any non-popup windows on Windows and Linux. Then we must
* resize such that we have at least one non-popup window.
*/
_capClosedWindows: function ssi_capClosedWindows() {
@@ -4744,25 +4761,25 @@ var SessionStoreInternal = {
*
* @param aTab
* The tab that will be "reset"
*/
_resetLocalTabRestoringState(aTab) {
let browser = aTab.linkedBrowser;
// Keep the tab's previous state for later in this method
- let previousState = browser.__SS_restoreState;
+ let previousState = TAB_STATE_FOR_BROWSER.get(browser);
if (!previousState) {
Cu.reportError("Given tab is not restoring.");
return;
}
// The browser is no longer in any sort of restoring state.
- delete browser.__SS_restoreState;
+ TAB_STATE_FOR_BROWSER.delete(browser);
aTab.removeAttribute("pending");
if (previousState == TAB_STATE_RESTORING) {
if (this._tabsRestoringCount)
this._tabsRestoringCount--;
} else if (previousState == TAB_STATE_NEEDS_RESTORE) {
// Make sure that the tab is removed from the list of tabs to restore.
@@ -4770,17 +4787,17 @@ var SessionStoreInternal = {
// for this tab.
TabRestoreQueue.remove(aTab);
}
},
_resetTabRestoringState(tab) {
let browser = tab.linkedBrowser;
- if (!browser.__SS_restoreState) {
+ if (!TAB_STATE_FOR_BROWSER.has(browser)) {
Cu.reportError("Given tab is not restoring.");
return;
}
browser.messageManager.sendAsyncMessage("SessionStore:resetRestore", {});
this._resetLocalTabRestoringState(tab);
},
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -20,22 +20,22 @@ ChromeUtils.defineModuleGetter(this, "Ut
/**
* Module that contains tab state collection methods.
*/
var TabState = Object.freeze({
update(browser, data) {
TabStateInternal.update(browser, data);
},
- collect(tab) {
- return TabStateInternal.collect(tab);
+ collect(tab, extData) {
+ return TabStateInternal.collect(tab, extData);
},
- clone(tab) {
- return TabStateInternal.clone(tab);
+ clone(tab, extData) {
+ return TabStateInternal.clone(tab, extData);
},
copyFromCache(browser, tabData, options) {
TabStateInternal.copyFromCache(browser, tabData, options);
},
});
var TabStateInternal = {
@@ -46,46 +46,51 @@ var TabStateInternal = {
TabStateCache.update(browser, data);
},
/**
* Collect data related to a single tab, synchronously.
*
* @param tab
* tabbrowser tab
+ * @param [extData]
+ * optional dictionary object, containing custom tab values.
*
* @returns {TabData} An object with the data for this tab. If the
* tab has not been invalidated since the last call to
* collect(aTab), the same object is returned.
*/
- collect(tab) {
- return this._collectBaseTabData(tab);
+ collect(tab, extData) {
+ return this._collectBaseTabData(tab, {extData});
},
/**
* Collect data related to a single tab, including private data.
* Use with caution.
*
* @param tab
* tabbrowser tab
+ * @param [extData]
+ * optional dictionary object, containing custom tab values.
*
* @returns {object} An object with the data for this tab. This data is never
* cached, it will always be read from the tab and thus be
* up-to-date.
*/
- clone(tab) {
- return this._collectBaseTabData(tab, {includePrivateData: true});
+ clone(tab, extData) {
+ return this._collectBaseTabData(tab, {extData, includePrivateData: true});
},
/**
* Collects basic tab data for a given tab.
*
* @param tab
* tabbrowser tab
* @param options (object)
+ * {extData: object} optional dictionary object, containing custom tab values
* {includePrivateData: true} to always include private data
*
* @returns {object} An object with the basic data for this tab.
*/
_collectBaseTabData(tab, options) {
let tabData = { entries: [], lastAccessed: tab.lastAccessed };
let browser = tab.linkedBrowser;
@@ -100,18 +105,18 @@ var TabStateInternal = {
tabData.muteReason = tab.muteReason;
}
tabData.mediaBlocked = browser.mediaBlocked;
// Save tab attributes.
tabData.attributes = TabAttributes.get(tab);
- if (tab.__SS_extdata) {
- tabData.extData = tab.__SS_extdata;
+ if (options.extData) {
+ tabData.extData = options.extData;
}
// Copy data from the tab state cache only if the tab has fully finished
// restoring. We don't want to overwrite data contained in __SS_data.
this.copyFromCache(browser, tabData, options);
// After copyFromCache() was called we check for properties that are kept
// in the cache only while the tab is pending or restoring. Once that
--- a/browser/components/sessionstore/test/browser_595601-restore_hidden.js
+++ b/browser/components/sessionstore/test/browser_595601-restore_hidden.js
@@ -72,28 +72,28 @@ var TabsProgressListener = {
this.callback = callback;
},
observe(browser) {
TabsProgressListener.onRestored(browser);
},
onRestored(browser) {
- if (this.callback && browser.__SS_restoreState == TAB_STATE_RESTORING)
+ if (this.callback && ss.getInternalObjectState(browser) == TAB_STATE_RESTORING)
this.callback.apply(null, [this.window].concat(this.countTabs()));
},
countTabs() {
let needsRestore = 0, isRestoring = 0;
for (let i = 0; i < this.window.gBrowser.tabs.length; i++) {
- let browser = this.window.gBrowser.tabs[i].linkedBrowser;
- if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+ let state = ss.getInternalObjectState(this.window.gBrowser.tabs[i].linkedBrowser);
+ if (state == TAB_STATE_RESTORING)
isRestoring++;
- else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ else if (state == TAB_STATE_NEEDS_RESTORE)
needsRestore++;
}
return [needsRestore, isRestoring];
}
};
// ----------
--- a/browser/components/sessionstore/test/browser_636279.js
+++ b/browser/components/sessionstore/test/browser_636279.js
@@ -53,20 +53,20 @@ function countTabs() {
let windowsEnum = Services.wm.getEnumerator("navigator:browser");
while (windowsEnum.hasMoreElements()) {
let window = windowsEnum.getNext();
if (window.closed)
continue;
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
- let browser = window.gBrowser.tabs[i].linkedBrowser;
- if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+ let browserState = ss.getInternalObjectState(window.gBrowser.tabs[i].linkedBrowser);
+ if (browserState == TAB_STATE_RESTORING)
isRestoring++;
- else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ else if (browserState == TAB_STATE_NEEDS_RESTORE)
needsRestore++;
}
}
return [needsRestore, isRestoring];
}
var TabsProgressListener = {
@@ -87,13 +87,13 @@ var TabsProgressListener = {
delete this.callback;
},
observe(browser, topic, data) {
TabsProgressListener.onRestored(browser);
},
onRestored(browser) {
- if (this.callback && browser.__SS_restoreState == TAB_STATE_RESTORING) {
+ if (this.callback && ss.getInternalObjectState(browser) == TAB_STATE_RESTORING) {
this.callback.apply(null, countTabs());
}
}
};
--- a/browser/components/sessionstore/test/browser_739805.js
+++ b/browser/components/sessionstore/test/browser_739805.js
@@ -18,17 +18,17 @@ function test() {
let tab = BrowserTestUtils.addTab(gBrowser, "about:blank");
let browser = tab.linkedBrowser;
promiseBrowserLoaded(browser).then(() => {
isnot(gBrowser.selectedTab, tab, "newly created tab is not selected");
ss.setTabState(tab, JSON.stringify(tabState));
- is(browser.__SS_restoreState, TAB_STATE_NEEDS_RESTORE, "tab needs restoring");
+ is(ss.getInternalObjectState(browser), TAB_STATE_NEEDS_RESTORE, "tab needs restoring");
let {formdata} = JSON.parse(ss.getTabState(tab));
is(formdata && formdata.id.foo, "bar", "tab state's formdata is valid");
promiseTabRestored(tab).then(() => {
ContentTask.spawn(browser, null, function() {
let input = content.document.getElementById("foo");
is(input.value, "bar", "formdata has been restored correctly");
--- a/browser/components/sessionstore/test/browser_pending_tabs.js
+++ b/browser/components/sessionstore/test/browser_pending_tabs.js
@@ -21,17 +21,17 @@ add_task(async function() {
ss.setTabState(tab, JSON.stringify(TAB_STATE));
ok(tab.hasAttribute("pending"), "tab is pending");
await promise;
// Flush to ensure the parent has all data.
await TabStateFlusher.flush(browser);
// Check that the shistory index is the one we restored.
- let tabState = TabState.collect(tab);
+ let tabState = TabState.collect(tab, ss.getInternalObjectState(tab));
is(tabState.index, TAB_STATE.index, "correct shistory index");
// Check we don't collect userTypedValue when we shouldn't.
ok(!tabState.userTypedValue, "tab didn't have a userTypedValue");
// Cleanup.
gBrowser.removeTab(tab);
});
--- a/browser/components/sessionstore/test/browser_restore_redirect.js
+++ b/browser/components/sessionstore/test/browser_restore_redirect.js
@@ -18,17 +18,17 @@ add_task(async function check_http_redir
info("Restored tab");
await TabStateFlusher.flush(browser);
let data = TabState.collect(tab);
is(data.entries.length, 1, "Should be one entry in session history");
is(data.entries[0].url, TARGET, "Should be the right session history entry");
- ok(!("__SS_data" in browser), "Temporary restore data should have been cleared");
+ ok(!ss.getInternalObjectState(browser), "Temporary restore data should have been cleared");
// Cleanup.
BrowserTestUtils.removeTab(tab);
});
/**
* Ensure that a js redirect leaves a working tab.
*/
@@ -57,13 +57,13 @@ add_task(async function check_js_redirec
await loadPromise;
await TabStateFlusher.flush(browser);
let data = TabState.collect(tab);
is(data.entries.length, 1, "Should be one entry in session history");
is(data.entries[0].url, TARGET, "Should be the right session history entry");
- ok(!("__SS_data" in browser), "Temporary restore data should have been cleared");
+ ok(!ss.getInternalObjectState(browser), "Temporary restore data should have been cleared");
// Cleanup.
BrowserTestUtils.removeTab(tab);
});
--- a/browser/components/sessionstore/test/browser_speculative_connect.js
+++ b/browser/components/sessionstore/test/browser_speculative_connect.js
@@ -25,42 +25,46 @@ add_task(async function speculative_conn
// Close the window.
await BrowserTestUtils.closeWindow(win);
// Reopen a window.
let newWin = undoCloseWindow(0);
// Make sure we wait until this window is restored.
await BrowserTestUtils.waitForEvent(newWin, "load");
- await BrowserTestUtils.waitForEvent(newWin.gBrowser.tabContainer, "SSTabRestored");
let tabs = newWin.gBrowser.tabs;
is(tabs.length, TEST_URLS.length + 1, "Restored right number of tabs");
let e = new MouseEvent("mouseover");
// First tab should be ignored, since it's the default blank tab when we open a new window.
// Trigger a mouse enter on second tab.
tabs[1].dispatchEvent(e);
- is(tabs[1].__test_connection_prepared, false, "Second tab doesn't have a connection prepared");
+ ok(!tabs[1].__test_connection_prepared, "Second tab doesn't have a connection prepared");
is(tabs[1].__test_connection_url, TEST_URLS[0], "Second tab has correct url");
- is(tabs[1].__SS_connectionPrepared, true, "Second tab should have __SS_connectionPrepared flag after hovered");
+ ok(SessionStore.getLazyTabValue(tabs[1], "connectionPrepared"),
+ "Second tab should have connectionPrepared flag after hovered");
// Trigger a mouse enter on third tab.
tabs[2].dispatchEvent(e);
- is(tabs[2].__test_connection_prepared, true, "Third tab has a connection prepared");
+ ok(tabs[2].__test_connection_prepared, "Third tab has a connection prepared");
is(tabs[2].__test_connection_url, TEST_URLS[1], "Third tab has correct url");
- is(tabs[2].__SS_connectionPrepared, true, "Third tab should have __SS_connectionPrepared flag after hovered");
+ ok(SessionStore.getLazyTabValue(tabs[2], "connectionPrepared"),
+ "Third tab should have connectionPrepared flag after hovered");
// Last tab is the previously selected tab.
tabs[3].dispatchEvent(e);
- is(tabs[3].__SS_connectionPrepared, undefined, "Previous selected tab shouldn't have __SS_connectionPrepared flag");
- is(tabs[3].__test_connection_prepared, undefined, "Previous selected tab should not have a connection prepared");
- is(tabs[3].__test_connection_url, undefined, "Previous selected tab should not have a connection prepared");
+ is(SessionStore.getLazyTabValue(tabs[3], "connectionPrepared"), undefined,
+ "Previous selected tab shouldn't have connectionPrepared flag");
+ is(tabs[3].__test_connection_prepared, undefined,
+ "Previous selected tab should not have a connection prepared");
+ is(tabs[3].__test_connection_url, undefined,
+ "Previous selected tab should not have a connection prepared");
await BrowserTestUtils.closeWindow(newWin);
});
add_task(async function speculative_connect_restore_automatically() {
Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
is(Services.prefs.getBoolPref("browser.sessionstore.restore_on_demand"), false, "We're restoring automatically");
forgetClosedWindows();
@@ -71,29 +75,28 @@ add_task(async function speculative_conn
// Close the window.
await BrowserTestUtils.closeWindow(win);
// Reopen a window.
let newWin = undoCloseWindow(0);
// Make sure we wait until this window is restored.
await BrowserTestUtils.waitForEvent(newWin, "load");
- await BrowserTestUtils.waitForEvent(newWin.gBrowser.tabContainer, "SSTabRestored");
let tabs = newWin.gBrowser.tabs;
is(tabs.length, TEST_URLS.length + 1, "Restored right number of tabs");
// First tab is ignored, since it's the default tab open when we open new window
// Second tab.
- is(tabs[1].__test_connection_prepared, false, "Second tab doesn't have a connection prepared");
+ ok(!tabs[1].__test_connection_prepared, "Second tab doesn't have a connection prepared");
is(tabs[1].__test_connection_url, TEST_URLS[0], "Second tab has correct host url");
// Third tab.
- is(tabs[2].__test_connection_prepared, true, "Third tab has a connection prepared");
+ ok(tabs[2].__test_connection_prepared, "Third tab has a connection prepared");
is(tabs[2].__test_connection_url, TEST_URLS[1], "Third tab has correct host url");
// Last tab is the previously selected tab.
is(tabs[3].__test_connection_prepared, undefined, "Selected tab should not have a connection prepared");
is(tabs[3].__test_connection_url, undefined, "Selected tab should not have a connection prepared");
await BrowserTestUtils.closeWindow(newWin);
});
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -359,33 +359,34 @@ var gProgressListener = {
}
},
observe(browser, topic, data) {
gProgressListener.onRestored(browser);
},
onRestored(browser) {
- if (browser.__SS_restoreState == TAB_STATE_RESTORING) {
+ if (ss.getInternalObjectState(browser) == TAB_STATE_RESTORING) {
let args = [browser].concat(gProgressListener._countTabs());
gProgressListener._callback.apply(gProgressListener, args);
}
},
_countTabs() {
let needsRestore = 0, isRestoring = 0, wasRestored = 0;
for (let win of BrowserWindowIterator()) {
for (let i = 0; i < win.gBrowser.tabs.length; i++) {
let browser = win.gBrowser.tabs[i].linkedBrowser;
- if (browser.isConnected && !browser.__SS_restoreState)
+ let state = ss.getInternalObjectState(browser);
+ if (browser.isConnected && !state)
wasRestored++;
- else if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+ else if (state == TAB_STATE_RESTORING)
isRestoring++;
- else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE || !browser.isConnected)
+ else if (state == TAB_STATE_NEEDS_RESTORE || !browser.isConnected)
needsRestore++;
}
}
return [needsRestore, isRestoring, wasRestored];
}
};
registerCleanupFunction(function() {