Backed out changesets 24c800ff4936 and 5e4e054f3c00 (bug 867143) for mochitest orange.

This commit is contained in:
Ryan VanderMeulen 2013-07-19 12:00:29 -04:00
Родитель c5d1e4d1ad
Коммит cb467ff94e
5 изменённых файлов: 184 добавлений и 492 удалений

Просмотреть файл

@ -13,7 +13,7 @@ function debug(msg) {
let EventListener = { let EventListener = {
DOM_EVENTS: [ DOM_EVENTS: [
"pageshow", "change", "input", "MozStorageChanged" "pageshow", "change", "input"
], ],
init: function () { init: function () {
@ -30,24 +30,6 @@ let EventListener = {
case "change": case "change":
sendAsyncMessage("SessionStore:input"); sendAsyncMessage("SessionStore:input");
break; break;
case "MozStorageChanged":
{
let isSessionStorage = true;
// We are only interested in sessionStorage events
try {
if (event.storageArea != content.sessionStorage) {
isSessionStorage = false;
}
} catch (ex) {
// This page does not even have sessionStorage
// (this is typically the case of about: pages)
isSessionStorage = false;
}
if (isSessionStorage) {
sendAsyncMessage("SessionStore:MozStorageChanged");
}
break;
}
default: default:
debug("received unknown event '" + event.type + "'"); debug("received unknown event '" + event.type + "'");
break; break;

Просмотреть файл

@ -58,11 +58,7 @@ const MESSAGES = [
// The content script has received a pageshow event. This happens when a // The content script has received a pageshow event. This happens when a
// page is loaded from bfcache without any network activity, i.e. when // page is loaded from bfcache without any network activity, i.e. when
// clicking the back or forward button. // clicking the back or forward button.
"SessionStore:pageshow", "SessionStore:pageshow"
// The content script has received a MozStorageChanged event dealing
// with a change in the contents of the sessionStorage.
"SessionStore:MozStorageChanged"
]; ];
// These are tab events that we listen to. // These are tab events that we listen to.
@ -124,16 +120,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
"@mozilla.org/xre/app-info;1", "nsICrashReporter"); "@mozilla.org/xre/app-info;1", "nsICrashReporter");
#endif #endif
/**
* |true| if we are in debug mode, |false| otherwise.
* Debug mode is controlled by preference browser.sessionstore.debug
*/
let gDebuggingEnabled = false;
function debug(aMsg) { function debug(aMsg) {
if (gDebuggingEnabled) { aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n"); Services.console.logStringMessage(aMsg);
Services.console.logStringMessage(aMsg);
}
} }
this.SessionStore = { this.SessionStore = {
@ -548,13 +537,9 @@ let SessionStoreInternal = {
}, },
_initPrefs : function() { _initPrefs : function() {
this._prefBranch = Services.prefs.getBranch("browser."); XPCOMUtils.defineLazyGetter(this, "_prefBranch", function () {
return Services.prefs.getBranch("browser.");
gDebuggingEnabled = this._prefBranch.getBoolPref("sessionstore.debug"); });
Services.prefs.addObserver("browser.sessionstore.debug", () => {
gDebuggingEnabled = this._prefBranch.getBoolPref("sessionstore.debug");
}, false);
// minimal interval between two save operations (in milliseconds) // minimal interval between two save operations (in milliseconds)
XPCOMUtils.defineLazyGetter(this, "_interval", function () { XPCOMUtils.defineLazyGetter(this, "_interval", function () {
@ -636,43 +621,37 @@ let SessionStoreInternal = {
if (this._disabledForMultiProcess) if (this._disabledForMultiProcess)
return; return;
try { switch (aTopic) {
switch (aTopic) { case "domwindowopened": // catch new windows
case "domwindowopened": // catch new windows this.onOpen(aSubject);
this.onOpen(aSubject); break;
break; case "domwindowclosed": // catch closed windows
case "domwindowclosed": // catch closed windows this.onClose(aSubject);
this.onClose(aSubject); break;
break; case "quit-application-requested":
case "quit-application-requested": this.onQuitApplicationRequested();
this.onQuitApplicationRequested(); break;
break; case "quit-application-granted":
case "quit-application-granted": this.onQuitApplicationGranted();
this.onQuitApplicationGranted(); break;
break; case "browser-lastwindow-close-granted":
case "browser-lastwindow-close-granted": this.onLastWindowCloseGranted();
this.onLastWindowCloseGranted(); break;
break; case "quit-application":
case "quit-application": this.onQuitApplication(aData);
this.onQuitApplication(aData); break;
break; case "browser:purge-session-history": // catch sanitization
case "browser:purge-session-history": // catch sanitization this.onPurgeSessionHistory();
this.onPurgeSessionHistory(); break;
break; case "browser:purge-domain-data":
case "browser:purge-domain-data": this.onPurgeDomainData(aData);
this.onPurgeDomainData(aData); break;
break; case "nsPref:changed": // catch pref changes
case "nsPref:changed": // catch pref changes this.onPrefChange(aData);
this.onPrefChange(aData); break;
break; case "timer-callback": // timer call back for delayed saving
case "timer-callback": // timer call back for delayed saving this.onTimerCallback();
this.onTimerCallback(); break;
break;
}
} catch (ex) {
debug("Uncaught error during observe");
debug(ex);
debug(ex.stack);
} }
}, },
@ -691,10 +670,6 @@ let SessionStoreInternal = {
case "SessionStore:input": case "SessionStore:input":
this.onTabInput(win, browser); this.onTabInput(win, browser);
break; break;
case "SessionStore:MozStorageChanged":
TabStateCache.delete(browser);
this.saveStateDelayed(win);
break;
default: default:
debug("received unknown message '" + aMessage.name + "'"); debug("received unknown message '" + aMessage.name + "'");
break; break;
@ -1116,7 +1091,6 @@ let SessionStoreInternal = {
let openWindows = {}; let openWindows = {};
this._forEachBrowserWindow(function(aWindow) { this._forEachBrowserWindow(function(aWindow) {
Array.forEach(aWindow.gBrowser.tabs, function(aTab) { Array.forEach(aWindow.gBrowser.tabs, function(aTab) {
TabStateCache.delete(aTab);
delete aTab.linkedBrowser.__SS_data; delete aTab.linkedBrowser.__SS_data;
delete aTab.linkedBrowser.__SS_tabStillLoading; delete aTab.linkedBrowser.__SS_tabStillLoading;
delete aTab.linkedBrowser.__SS_formDataSaved; delete aTab.linkedBrowser.__SS_formDataSaved;
@ -1340,8 +1314,9 @@ let SessionStoreInternal = {
return; return;
} }
// Get the latest data for this tab (generally, from the cache) // make sure that the tab related data is up-to-date
let tabState = this._collectTabData(aTab); var tabState = this._collectTabData(aTab);
this._updateTextAndScrollDataForTab(aWindow, aTab.linkedBrowser, tabState);
// store closed-tab data for undo // store closed-tab data for undo
if (this._shouldSaveTabState(tabState)) { if (this._shouldSaveTabState(tabState)) {
@ -1362,8 +1337,7 @@ let SessionStoreInternal = {
}, },
/** /**
* When a tab loads, invalidate its cached state, trigger async save. * When a tab loads, save state.
*
* @param aWindow * @param aWindow
* Window reference * Window reference
* @param aBrowser * @param aBrowser
@ -1379,8 +1353,6 @@ let SessionStoreInternal = {
return; return;
} }
TabStateCache.delete(aBrowser);
delete aBrowser.__SS_data; delete aBrowser.__SS_data;
delete aBrowser.__SS_tabStillLoading; delete aBrowser.__SS_tabStillLoading;
delete aBrowser.__SS_formDataSaved; delete aBrowser.__SS_formDataSaved;
@ -1401,8 +1373,6 @@ let SessionStoreInternal = {
// deleting __SS_formDataSaved will cause us to recollect form data // deleting __SS_formDataSaved will cause us to recollect form data
delete aBrowser.__SS_formDataSaved; delete aBrowser.__SS_formDataSaved;
TabStateCache.delete(aBrowser);
this.saveStateDelayed(aWindow, 3000); this.saveStateDelayed(aWindow, 3000);
}, },
@ -1526,41 +1496,20 @@ let SessionStoreInternal = {
if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi) if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
let tabState = this._collectTabData(aTab); var tabState = this._collectTabData(aTab);
var window = aTab.ownerDocument.defaultView;
this._updateTextAndScrollDataForTab(window, aTab.linkedBrowser, tabState);
return this._toJSONString(tabState); return this._toJSONString(tabState);
}, },
setTabState: function ssi_setTabState(aTab, aState) { setTabState: function ssi_setTabState(aTab, aState) {
// Remove the tab state from the cache. var tabState = JSON.parse(aState);
// Note that we cannot simply replace the contents of the cache if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
// as |aState| can be an incomplete state that will be completed
// by |restoreHistoryPrecursor|.
let tabState = JSON.parse(aState);
if (!tabState) {
debug("Empty state argument");
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
if (typeof tabState != "object") {
debug("State argument does not represent an object");
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
if (!("entries" in tabState)) {
debug("State argument must contain field 'entries'");
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
if (!aTab.ownerDocument) {
debug("Tab argument must have an owner document");
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
let window = aTab.ownerDocument.defaultView; var window = aTab.ownerDocument.defaultView;
if (!("__SSi" in window)) {
debug("Default view of ownerDocument must have a unique identifier");
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
}
TabStateCache.delete(aTab);
this._setWindowStateBusy(window); this._setWindowStateBusy(window);
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0); this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
}, },
@ -1570,9 +1519,9 @@ let SessionStoreInternal = {
!aWindow.getBrowser) !aWindow.getBrowser)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG); throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
// Duplicate the tab state var tabState = this._collectTabData(aTab, true);
let tabState = this._cloneFullTabData(aTab); var sourceWindow = aTab.ownerDocument.defaultView;
this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true);
tabState.index += aDelta; tabState.index += aDelta;
tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length)); tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
tabState.pinned = false; tabState.pinned = false;
@ -1742,7 +1691,6 @@ let SessionStoreInternal = {
if (aWindow.__SSi && this._windows[aWindow.__SSi].extData && if (aWindow.__SSi && this._windows[aWindow.__SSi].extData &&
this._windows[aWindow.__SSi].extData[aKey]) this._windows[aWindow.__SSi].extData[aKey])
delete this._windows[aWindow.__SSi].extData[aKey]; delete this._windows[aWindow.__SSi].extData[aKey];
this.saveStateDelayed(aWindow);
}, },
getTabValue: function ssi_getTabValue(aTab, aKey) { getTabValue: function ssi_getTabValue(aTab, aKey) {
@ -1758,7 +1706,6 @@ let SessionStoreInternal = {
}, },
setTabValue: function ssi_setTabValue(aTab, aKey, aStringValue) { setTabValue: function ssi_setTabValue(aTab, aKey, aStringValue) {
TabStateCache.delete(aTab);
// If the tab hasn't been restored, then set the data there, otherwise we // If the tab hasn't been restored, then set the data there, otherwise we
// could lose newly added data. // could lose newly added data.
let saveTo; let saveTo;
@ -1777,7 +1724,6 @@ let SessionStoreInternal = {
}, },
deleteTabValue: function ssi_deleteTabValue(aTab, aKey) { deleteTabValue: function ssi_deleteTabValue(aTab, aKey) {
TabStateCache.delete(aTab);
// We want to make sure that if data is accessed early, we attempt to delete // We want to make sure that if data is accessed early, we attempt to delete
// that data from __SS_data as well. Otherwise we'll throw in cases where // that data from __SS_data as well. Otherwise we'll throw in cases where
// data can be set or read. // data can be set or read.
@ -1791,7 +1737,6 @@ let SessionStoreInternal = {
if (deleteFrom && deleteFrom[aKey]) if (deleteFrom && deleteFrom[aKey])
delete deleteFrom[aKey]; delete deleteFrom[aKey];
this.saveStateDelayed(aTab.ownerDocument.defaultView);
}, },
persistTabAttribute: function ssi_persistTabAttribute(aName) { persistTabAttribute: function ssi_persistTabAttribute(aName) {
@ -1969,51 +1914,32 @@ let SessionStoreInternal = {
/* ........ Saving Functionality .............. */ /* ........ Saving Functionality .............. */
/** /**
* Collect data related to a single tab * Store all session data for a window
* * @param aWindow
* @param aTab * Window reference
* tabbrowser tab
*
* @returns {TabData} An object with the data for this tab. If the
* tab has not been invalidated since the last call to
* _collectTabData(aTab), the same object is returned.
*/ */
_collectTabData: function ssi_collectTabData(aTab) { _saveWindowHistory: function ssi_saveWindowHistory(aWindow) {
if (!aTab) { var tabbrowser = aWindow.gBrowser;
throw new TypeError("Expecting a tab"); var tabs = tabbrowser.tabs;
} var tabsData = this._windows[aWindow.__SSi].tabs = [];
let tabData;
if ((tabData = TabStateCache.get(aTab))) {
return tabData;
}
tabData = new TabData(this._collectBaseTabData(aTab));
TabStateCache.set(aTab, tabData);
this._updateTextAndScrollDataForTab(aTab, tabData); for (var i = 0; i < tabs.length; i++)
return tabData; tabsData.push(this._collectTabData(tabs[i]));
this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1;
}, },
/** /**
* Collect data related to a single tab, including private data. * Collect data related to a single tab
* Use with caution.
*
* @param aTab * @param aTab
* tabbrowser tab * tabbrowser tab
* * @param aFullData
* @returns {object} An object with the data for this tab. This object * always return privacy sensitive data (use with care)
* is recomputed at every call. * @returns object
*/ */
_cloneFullTabData: function ssi_cloneFullTabData(aTab) { _collectTabData: function ssi_collectTabData(aTab, aFullData) {
let options = { includePrivateData: true }; var tabData = { entries: [], lastAccessed: aTab.lastAccessed };
let tabData = this._collectBaseTabData(aTab, options); var browser = aTab.linkedBrowser;
this._updateTextAndScrollDataForTab(aTab, tabData, options);
return tabData;
},
_collectBaseTabData: function ssi_collectBaseTabData(aTab, aOptions = null) {
let includePrivateData = aOptions && aOptions.includePrivateData;
let tabData = {entries: [], lastAccessed: aTab.lastAccessed };
let browser = aTab.linkedBrowser;
if (!browser || !browser.currentURI) if (!browser || !browser.currentURI)
// can happen when calling this function right after .addTab() // can happen when calling this function right after .addTab()
@ -2048,7 +1974,7 @@ let SessionStoreInternal = {
if (history && browser.__SS_data && if (history && browser.__SS_data &&
browser.__SS_data.entries[history.index] && browser.__SS_data.entries[history.index] &&
browser.__SS_data.entries[history.index].url == browser.currentURI.spec && browser.__SS_data.entries[history.index].url == browser.currentURI.spec &&
history.index < this._sessionhistory_max_entries - 1 && !includePrivateData) { history.index < this._sessionhistory_max_entries - 1 && !aFullData) {
tabData = browser.__SS_data; tabData = browser.__SS_data;
tabData.index = history.index + 1; tabData.index = history.index + 1;
} }
@ -2057,7 +1983,7 @@ let SessionStoreInternal = {
try { try {
for (var j = 0; j < history.count; j++) { for (var j = 0; j < history.count; j++) {
let entry = this._serializeHistoryEntry(history.getEntryAtIndex(j, false), let entry = this._serializeHistoryEntry(history.getEntryAtIndex(j, false),
includePrivateData, aTab.pinned, browser.__SS_hostSchemeData); aFullData, aTab.pinned, browser.__SS_hostSchemeData);
tabData.entries.push(entry); tabData.entries.push(entry);
} }
// If we make it through the for loop, then we're ok and we should clear // If we make it through the for loop, then we're ok and we should clear
@ -2083,7 +2009,7 @@ let SessionStoreInternal = {
tabData.index = history.index + 1; tabData.index = history.index + 1;
// make sure not to cache privacy sensitive data which shouldn't get out // make sure not to cache privacy sensitive data which shouldn't get out
if (!includePrivateData) if (!aFullData)
browser.__SS_data = tabData; browser.__SS_data = tabData;
} }
else if (browser.currentURI.spec != "about:blank" || else if (browser.currentURI.spec != "about:blank" ||
@ -2132,7 +2058,7 @@ let SessionStoreInternal = {
delete tabData.extData; delete tabData.extData;
if (history && browser.docShell instanceof Ci.nsIDocShell) { if (history && browser.docShell instanceof Ci.nsIDocShell) {
let storageData = SessionStorage.serialize(browser.docShell, includePrivateData) let storageData = SessionStorage.serialize(browser.docShell, aFullData)
if (Object.keys(storageData).length) if (Object.keys(storageData).length)
tabData.storage = storageData; tabData.storage = storageData;
} }
@ -2145,7 +2071,7 @@ let SessionStoreInternal = {
* Used for data storage * Used for data storage
* @param aEntry * @param aEntry
* nsISHEntry instance * nsISHEntry instance
* @param aIncludePrivateData * @param aFullData
* always return privacy sensitive data (use with care) * always return privacy sensitive data (use with care)
* @param aIsPinned * @param aIsPinned
* the tab is pinned and should be treated differently for privacy * the tab is pinned and should be treated differently for privacy
@ -2154,7 +2080,7 @@ let SessionStoreInternal = {
* @returns object * @returns object
*/ */
_serializeHistoryEntry: _serializeHistoryEntry:
function ssi_serializeHistoryEntry(aEntry, aIncludePrivateData, aIsPinned, aHostSchemeData) { function ssi_serializeHistoryEntry(aEntry, aFullData, aIsPinned, aHostSchemeData) {
var entry = { url: aEntry.URI.spec }; var entry = { url: aEntry.URI.spec };
try { try {
@ -2205,7 +2131,7 @@ let SessionStoreInternal = {
try { try {
var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata"); var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata");
if (aEntry.postData && (aIncludePrivateData || prefPostdata && if (aEntry.postData && (aFullData || prefPostdata &&
this.checkPrivacyLevel(aEntry.URI.schemeIs("https"), aIsPinned))) { this.checkPrivacyLevel(aEntry.URI.schemeIs("https"), aIsPinned))) {
aEntry.postData.QueryInterface(Ci.nsISeekableStream). aEntry.postData.QueryInterface(Ci.nsISeekableStream).
seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
@ -2214,7 +2140,7 @@ let SessionStoreInternal = {
stream.setInputStream(aEntry.postData); stream.setInputStream(aEntry.postData);
var postBytes = stream.readByteArray(stream.available()); var postBytes = stream.readByteArray(stream.available());
var postdata = String.fromCharCode.apply(null, postBytes); var postdata = String.fromCharCode.apply(null, postBytes);
if (aIncludePrivateData || prefPostdata == -1 || if (aFullData || prefPostdata == -1 ||
postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <= postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <=
prefPostdata) { prefPostdata) {
// We can stop doing base64 encoding once our serialization into JSON // We can stop doing base64 encoding once our serialization into JSON
@ -2275,7 +2201,7 @@ let SessionStoreInternal = {
break; break;
} }
children.push(this._serializeHistoryEntry(child, aIncludePrivateData, children.push(this._serializeHistoryEntry(child, aFullData,
aIsPinned, aHostSchemeData)); aIsPinned, aHostSchemeData));
} }
} }
@ -2288,25 +2214,37 @@ let SessionStoreInternal = {
}, },
/** /**
* Go through all frames and store the current scroll positions * go through all tabs and store the current scroll positions
* and innerHTML content of WYSIWYG editors * and innerHTML content of WYSIWYG editors
* * @param aWindow
* @param aTab * Window reference
* tabbrowser tab */
_updateTextAndScrollData: function ssi_updateTextAndScrollData(aWindow) {
var browsers = aWindow.gBrowser.browsers;
this._windows[aWindow.__SSi].tabs.forEach(function (tabData, i) {
try {
this._updateTextAndScrollDataForTab(aWindow, browsers[i], tabData);
}
catch (ex) { debug(ex); } // get as much data as possible, ignore failures (might succeed the next time)
}, this);
},
/**
* go through all frames and store the current scroll positions
* and innerHTML content of WYSIWYG editors
* @param aWindow
* Window reference
* @param aBrowser
* single browser reference
* @param aTabData * @param aTabData
* tabData object to add the information to * tabData object to add the information to
* @param options * @param aFullData
* An optional object that may contain the following field: * always return privacy sensitive data (use with care)
* - includePrivateData: always return privacy sensitive data
* (use with care)
*/ */
_updateTextAndScrollDataForTab: _updateTextAndScrollDataForTab:
function ssi_updateTextAndScrollDataForTab(aTab, aTabData, aOptions = null) { function ssi_updateTextAndScrollDataForTab(aWindow, aBrowser, aTabData, aFullData) {
let includePrivateData = aOptions && aOptions.includePrivateData;
let window = aTab.ownerDocument.defaultView;
let browser = aTab.linkedBrowser;
// we shouldn't update data for incompletely initialized tabs // we shouldn't update data for incompletely initialized tabs
if (browser.__SS_data && browser.__SS_tabStillLoading) if (aBrowser.__SS_data && aBrowser.__SS_tabStillLoading)
return; return;
var tabIndex = (aTabData.index || aTabData.entries.length) - 1; var tabIndex = (aTabData.index || aTabData.entries.length) - 1;
@ -2314,22 +2252,22 @@ let SessionStoreInternal = {
if (!aTabData.entries[tabIndex]) if (!aTabData.entries[tabIndex])
return; return;
let selectedPageStyle = browser.markupDocumentViewer.authorStyleDisabled ? "_nostyle" : let selectedPageStyle = aBrowser.markupDocumentViewer.authorStyleDisabled ? "_nostyle" :
this._getSelectedPageStyle(browser.contentWindow); this._getSelectedPageStyle(aBrowser.contentWindow);
if (selectedPageStyle) if (selectedPageStyle)
aTabData.pageStyle = selectedPageStyle; aTabData.pageStyle = selectedPageStyle;
else if (aTabData.pageStyle) else if (aTabData.pageStyle)
delete aTabData.pageStyle; delete aTabData.pageStyle;
this._updateTextAndScrollDataForFrame(window, browser.contentWindow, this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
aTabData.entries[tabIndex], aTabData.entries[tabIndex],
!browser.__SS_formDataSaved, includePrivateData, !aBrowser.__SS_formDataSaved, aFullData,
!!aTabData.pinned); !!aTabData.pinned);
browser.__SS_formDataSaved = true; aBrowser.__SS_formDataSaved = true;
if (browser.currentURI.spec == "about:config") if (aBrowser.currentURI.spec == "about:config")
aTabData.entries[tabIndex].formdata = { aTabData.entries[tabIndex].formdata = {
id: { id: {
"textbox": browser.contentDocument.getElementById("textbox").value "textbox": aBrowser.contentDocument.getElementById("textbox").value
}, },
xpath: {} xpath: {}
}; };
@ -2346,26 +2284,26 @@ let SessionStoreInternal = {
* part of a tabData object to add the information to * part of a tabData object to add the information to
* @param aUpdateFormData * @param aUpdateFormData
* update all form data for this tab * update all form data for this tab
* @param aIncludePrivateData * @param aFullData
* always return privacy sensitive data (use with care) * always return privacy sensitive data (use with care)
* @param aIsPinned * @param aIsPinned
* the tab is pinned and should be treated differently for privacy * the tab is pinned and should be treated differently for privacy
*/ */
_updateTextAndScrollDataForFrame: _updateTextAndScrollDataForFrame:
function ssi_updateTextAndScrollDataForFrame(aWindow, aContent, aData, function ssi_updateTextAndScrollDataForFrame(aWindow, aContent, aData,
aUpdateFormData, aIncludePrivateData, aIsPinned) { aUpdateFormData, aFullData, aIsPinned) {
for (var i = 0; i < aContent.frames.length; i++) { for (var i = 0; i < aContent.frames.length; i++) {
if (aData.children && aData.children[i]) if (aData.children && aData.children[i])
this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i], this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i],
aData.children[i], aUpdateFormData, aData.children[i], aUpdateFormData,
aIncludePrivateData, aIsPinned); aFullData, aIsPinned);
} }
var isHTTPS = this._getURIFromString((aContent.parent || aContent). var isHTTPS = this._getURIFromString((aContent.parent || aContent).
document.location.href).schemeIs("https"); document.location.href).schemeIs("https");
let topURL = aContent.top.document.location.href; let topURL = aContent.top.document.location.href;
let isAboutSR = topURL == "about:sessionrestore" || topURL == "about:welcomeback"; let isAboutSR = topURL == "about:sessionrestore" || topURL == "about:welcomeback";
if (aIncludePrivateData || this.checkPrivacyLevel(isHTTPS, aIsPinned) || isAboutSR) { if (aFullData || this.checkPrivacyLevel(isHTTPS, aIsPinned) || isAboutSR) {
if (aIncludePrivateData || aUpdateFormData) { if (aFullData || aUpdateFormData) {
let formData = DocumentUtils.getFormData(aContent.document); let formData = DocumentUtils.getFormData(aContent.document);
// We want to avoid saving data for about:sessionrestore as a string. // We want to avoid saving data for about:sessionrestore as a string.
@ -2488,6 +2426,28 @@ let SessionStoreInternal = {
} }
}, },
/**
* store all hosts for a URL
* @param aWindow
* Window reference
*/
_updateCookieHosts: function ssi_updateCookieHosts(aWindow) {
var hosts = this._internalWindows[aWindow.__SSi].hosts = {};
// Since _updateCookiesHosts is only ever called for open windows during a
// session, we can call into _extractHostsForCookiesFromHostScheme directly
// using data that is attached to each browser.
for (let i = 0; i < aWindow.gBrowser.tabs.length; i++) {
let tab = aWindow.gBrowser.tabs[i];
let hostSchemeData = tab.linkedBrowser.__SS_hostSchemeData || [];
for (let j = 0; j < hostSchemeData.length; j++) {
this._extractHostsForCookiesFromHostScheme(hostSchemeData[j].host,
hostSchemeData[j].scheme,
hosts, true, tab.pinned);
}
}
},
/** /**
* Serialize cookie data * Serialize cookie data
* @param aWindows * @param aWindows
@ -2725,29 +2685,10 @@ let SessionStoreInternal = {
if (!this._isWindowLoaded(aWindow)) if (!this._isWindowLoaded(aWindow))
return; return;
let tabbrowser = aWindow.gBrowser;
let tabs = tabbrowser.tabs;
let winData = this._windows[aWindow.__SSi];
let tabsData = winData.tabs = [];
let hosts = this._internalWindows[aWindow.__SSi].hosts = {};
// update the internal state data for this window // update the internal state data for this window
for (let tab of tabs) { this._saveWindowHistory(aWindow);
tabsData.push(this._collectTabData(tab)); this._updateTextAndScrollData(aWindow);
this._updateCookieHosts(aWindow);
// Since we are only ever called for open
// windows during a session, we can call into
// _extractHostsForCookiesFromHostScheme directly using data
// that is attached to each browser.
let hostSchemeData = tab.linkedBrowser.__SS_hostSchemeData || [];
for (let j = 0; j < hostSchemeData.length; j++) {
this._extractHostsForCookiesFromHostScheme(hostSchemeData[j].host,
hostSchemeData[j].scheme,
hosts, true, tab.pinned);
}
}
winData.selected = tabbrowser.mTabBox.selectedIndex + 1;
this._updateWindowFeatures(aWindow); this._updateWindowFeatures(aWindow);
// Make sure we keep __SS_lastSessionWindowID around for cases like entering // Make sure we keep __SS_lastSessionWindowID around for cases like entering
@ -3046,7 +2987,6 @@ let SessionStoreInternal = {
restoreHistoryPrecursor: restoreHistoryPrecursor:
function ssi_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, function ssi_restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab,
aIx, aCount, aRestoreImmediately = false) { aIx, aCount, aRestoreImmediately = false) {
var tabbrowser = aWindow.gBrowser; var tabbrowser = aWindow.gBrowser;
// make sure that all browsers and their histories are available // make sure that all browsers and their histories are available
@ -3062,7 +3002,7 @@ let SessionStoreInternal = {
var restoreHistoryFunc = function(self) { var restoreHistoryFunc = function(self) {
self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab, self.restoreHistoryPrecursor(aWindow, aTabs, aTabData, aSelectTab,
aIx, aCount + 1, aRestoreImmediately); aIx, aCount + 1, aRestoreImmediately);
}; }
aWindow.setTimeout(restoreHistoryFunc, 100, this); aWindow.setTimeout(restoreHistoryFunc, 100, this);
return; return;
} }
@ -3198,6 +3138,7 @@ let SessionStoreInternal = {
var tab = aTabs.shift(); var tab = aTabs.shift();
var tabData = aTabData.shift(); var tabData = aTabData.shift();
var browser = aWindow.gBrowser.getBrowserForTab(tab); var browser = aWindow.gBrowser.getBrowserForTab(tab);
var history = browser.webNavigation.sessionHistory; var history = browser.webNavigation.sessionHistory;
@ -3762,7 +3703,7 @@ let SessionStoreInternal = {
* @param aDelay * @param aDelay
* Milliseconds to delay * Milliseconds to delay
*/ */
saveStateDelayed: function ssi_saveStateDelayed(aWindow = null, aDelay = 2000) { saveStateDelayed: function ssi_saveStateDelayed(aWindow, aDelay) {
if (aWindow) { if (aWindow) {
this._dirtyWindows[aWindow.__SSi] = true; this._dirtyWindows[aWindow.__SSi] = true;
} }
@ -3772,7 +3713,7 @@ let SessionStoreInternal = {
var minimalDelay = this._lastSaveTime + this._interval - Date.now(); var minimalDelay = this._lastSaveTime + this._interval - Date.now();
// if we have to wait, set a timer, otherwise saveState directly // if we have to wait, set a timer, otherwise saveState directly
aDelay = Math.max(minimalDelay, aDelay); aDelay = Math.max(minimalDelay, aDelay || 2000);
if (aDelay > 0) { if (aDelay > 0) {
this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT); this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
@ -4044,7 +3985,6 @@ let SessionStoreInternal = {
if (tab.linkedBrowser == aBrowser) if (tab.linkedBrowser == aBrowser)
return tab; return tab;
} }
return undefined;
}, },
/** /**
@ -4867,25 +4807,11 @@ SessionStoreSHistoryListener.prototype = {
Ci.nsISupportsWeakReference Ci.nsISupportsWeakReference
]), ]),
browser: null, browser: null,
// The following events (with the exception of OnHistoryPurge) OnHistoryNewEntry: function(aNewURI) { },
// accompany either a "load" or a "pageshow" which will in turn cause OnHistoryGoBack: function(aBackURI) { return true; },
// invalidations. OnHistoryGoForward: function(aForwardURI) { return true; },
OnHistoryNewEntry: function(aNewURI) { OnHistoryGotoIndex: function(aIndex, aGotoURI) { return true; },
OnHistoryPurge: function(aNumEntries) { return true; },
},
OnHistoryGoBack: function(aBackURI) {
return true;
},
OnHistoryGoForward: function(aForwardURI) {
return true;
},
OnHistoryGotoIndex: function(aIndex, aGotoURI) {
return true;
},
OnHistoryPurge: function(aNumEntries) {
TabStateCache.delete(this.tab);
return true;
},
OnHistoryReload: function(aReloadURI, aReloadFlags) { OnHistoryReload: function(aReloadURI, aReloadFlags) {
// On reload, we want to make sure that session history loads the right // On reload, we want to make sure that session history loads the right
// URI. In order to do that, we will juet call restoreTab. That will remove // URI. In order to do that, we will juet call restoreTab. That will remove
@ -4908,83 +4834,4 @@ String.prototype.hasRootDomain = function hasRootDomain(aDomain) {
let prevChar = this[index - 1]; let prevChar = this[index - 1];
return (index == (this.length - aDomain.length)) && return (index == (this.length - aDomain.length)) &&
(prevChar == "." || prevChar == "/"); (prevChar == "." || prevChar == "/");
};
function TabData(obj = null) {
if (obj) {
if (obj instanceof TabData) {
// FIXME: Can we get rid of this?
return obj;
}
for (let [key, value] in Iterator(obj)) {
this[key] = value;
}
}
return this;
} }
/**
* A cache for tabs data.
*
* This cache implements a weak map from tabs (as XUL elements)
* to tab data (as instances of TabData).
*
* Note that we should never cache private data, as:
* - that data is used very seldom by SessionStore;
* - caching private data in addition to public data is memory consuming.
*/
let TabStateCache = {
_data: new WeakMap(),
/**
* Add or replace an entry in the cache.
*
* @param {XULElement} aTab The key, which may be either a tab
* or the corresponding browser. The binding will disappear
* if the tab/browser is destroyed.
* @param {TabData} aValue The data associated to |aTab|.
*/
set: function(aTab, aValue) {
let key = this._normalizeToBrowser(aTab);
if (!(aValue instanceof TabData)) {
throw new TypeError("Attempting to cache a non TabData");
}
this._data.set(key, aValue);
},
/**
* Return the tab data associated with a tab.
*
* @param {XULElement} aKey The tab or the associated browser.
*
* @return {TabData|undefined} The data if available, |undefined|
* otherwise.
*/
get: function(aKey) {
let key = this._normalizeToBrowser(aKey);
return this._data.get(key);
},
/**
* Delete the tab data associated with a tab.
*
* @param {XULElement} aKey The tab or the associated browser.
*
* Noop of there is no tab data associated with the tab.
*/
delete: function(aKey) {
let key = this._normalizeToBrowser(aKey);
this._data.delete(key);
},
_normalizeToBrowser: function(aKey) {
let nodeName = aKey.localName;
if (nodeName == "tab") {
return aKey.linkedBrowser;
}
if (nodeName == "browser") {
return aKey;
}
throw new TypeError("Key is neither a tab nor a browser: " + nodeName);
}
};

Просмотреть файл

@ -27,7 +27,6 @@ MOCHITEST_BROWSER_FILES = \
browser_input.js \ browser_input.js \
browser_input_sample.html \ browser_input_sample.html \
browser_pageshow.js \ browser_pageshow.js \
browser_sessionStorage.js \
browser_upgrade_backup.js \ browser_upgrade_backup.js \
browser_windowRestore_perwindowpb.js \ browser_windowRestore_perwindowpb.js \
browser_248970_b_perwindowpb.js \ browser_248970_b_perwindowpb.js \

Просмотреть файл

@ -1,89 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let Scope = {};
Cu.import("resource://gre/modules/Task.jsm", Scope);
Cu.import("resource://gre/modules/Promise.jsm", Scope);
let {Task, Promise} = Scope;
function promiseBrowserLoaded(aBrowser) {
let deferred = Promise.defer();
whenBrowserLoaded(aBrowser, () => deferred.resolve());
return deferred.promise;
}
function forceWriteState() {
let deferred = Promise.defer();
const PREF = "browser.sessionstore.interval";
const TOPIC = "sessionstore-state-write";
Services.obs.addObserver(function observe() {
Services.obs.removeObserver(observe, TOPIC);
Services.prefs.clearUserPref(PREF);
deferred.resolve();
}, TOPIC, false);
Services.prefs.setIntPref(PREF, 0);
return deferred.promise;
}
function waitForStorageChange(aTab) {
let deferred = Promise.defer();
waitForContentMessage(aTab.linkedBrowser,
"sessionstore:MozStorageChanged",
200,
((x) => deferred.resolve(x)));
return deferred.promise;
}
function test() {
waitForExplicitFinish();
let tab;
Task.spawn(function() {
try {
tab = gBrowser.addTab("http://example.com");
// about:home supports sessionStorage and localStorage
let win = tab.linkedBrowser.contentWindow;
// Flush loading and next save, call getBrowserState()
// a few times to ensure that everything is cached.
yield promiseBrowserLoaded(tab.linkedBrowser);
yield forceWriteState();
info("Calling getBrowserState() to populate cache");
ss.getBrowserState();
info("Change sessionStorage, ensure that state is saved");
win.sessionStorage["SESSION_STORAGE_KEY"] = "SESSION_STORAGE_VALUE";
yield waitForStorageChange(tab);
yield forceWriteState();
let state = ss.getBrowserState();
ok(state.indexOf("SESSION_STORAGE_KEY") != -1, "Key appears in state");
ok(state.indexOf("SESSION_STORAGE_VALUE") != -1, "Value appears in state");
info("Change localStorage, ensure that state is not saved");
win.localStorage["LOCAL_STORAGE_KEY"] = "LOCAL_STORAGE_VALUE";
yield waitForStorageChange(tab);
yield forceWriteState();
state = ss.getBrowserState();
ok(state.indexOf("LOCAL_STORAGE_KEY") == -1, "Key does not appear in state");
ok(state.indexOf("LOCAL_STORAGE_VALUE") == -1, "Value does not appear in state");
} catch (ex) {
ok(false, ex);
info(ex.stack);
} finally {
// clean up
if (tab) {
gBrowser.removeTab(tab);
}
executeSoon(finish);
}
});
}

Просмотреть файл

@ -156,90 +156,43 @@ function waitForTabState(aTab, aState, aCallback) {
ss.setTabState(aTab, JSON.stringify(aState)); ss.setTabState(aTab, JSON.stringify(aState));
} }
/** // waitForSaveState waits for a state write but not necessarily for the state to
* Wait for a content -> chrome message. // turn dirty.
*/ function waitForSaveState(aSaveStateCallback) {
function waitForContentMessage(aBrowser, aTopic, aTimeout, aCallback) {
let mm = aBrowser.messageManager;
let observing = false; let observing = false;
function removeObserver() { let topic = "sessionstore-state-write";
if (!observing)
return;
mm.removeMessageListener(aTopic, observer);
observing = false;
}
let timeout = setTimeout(function () { let sessionSaveTimeout = 1000 +
removeObserver();
aCallback(false);
}, aTimeout);
function observer(aSubject, aTopic, aData) {
removeObserver();
timeout = clearTimeout(timeout);
executeSoon(() => aCallback(true));
}
registerCleanupFunction(function() {
removeObserver();
if (timeout) {
clearTimeout(timeout);
}
});
observing = true;
mm.addMessageListener(aTopic, observer);
}
function waitForTopic(aTopic, aTimeout, aCallback) {
let observing = false;
function removeObserver() {
if (!observing)
return;
Services.obs.removeObserver(observer, aTopic);
observing = false;
}
let timeout = setTimeout(function () {
removeObserver();
aCallback(false);
}, aTimeout);
function observer(aSubject, aTopic, aData) {
removeObserver();
timeout = clearTimeout(timeout);
executeSoon(() => aCallback(true));
}
registerCleanupFunction(function() {
removeObserver();
if (timeout) {
clearTimeout(timeout);
}
});
observing = true;
Services.obs.addObserver(observer, aTopic, false);
}
/**
* Wait until session restore has finished collecting its data and is
* getting ready to write that data ("sessionstore-state-write").
*
* This function is meant to be called immediately after the code
* that will trigger the saving.
*
* Note that this does not wait for the disk write to be complete.
*
* @param {function} aCallback If sessionstore-state-write is sent
* within buffering interval + 100 ms, the callback is passed |true|,
* otherwise, it is passed |false|.
*/
function waitForSaveState(aCallback) {
let timeout = 100 +
Services.prefs.getIntPref("browser.sessionstore.interval"); Services.prefs.getIntPref("browser.sessionstore.interval");
return waitForTopic("sessionstore-state-write", timeout, aCallback);
} function removeObserver() {
if (!observing)
return;
Services.obs.removeObserver(observer, topic);
observing = false;
}
let timeout = setTimeout(function () {
removeObserver();
aSaveStateCallback();
}, sessionSaveTimeout);
function observer(aSubject, aTopic, aData) {
removeObserver();
timeout = clearTimeout(timeout);
executeSoon(aSaveStateCallback);
}
registerCleanupFunction(function() {
removeObserver();
if (timeout) {
clearTimeout(timeout);
}
});
observing = true;
Services.obs.addObserver(observer, topic, false);
};
function whenBrowserLoaded(aBrowser, aCallback = next) { function whenBrowserLoaded(aBrowser, aCallback = next) {
aBrowser.addEventListener("load", function onLoad() { aBrowser.addEventListener("load", function onLoad() {