author | Tim Taubert <tim.taubert@gmx.de> |
Sun, 03 Jun 2012 11:45:51 +0200 | |
changeset 95679 | ebd52606c461f5ada17e67e6a089ba8f2c578928 |
parent 95639 | d0ebcaa7efb5fc96b2a4f7ef08434f6af969e4f2 |
child 95680 | 329aa567fec9b634602db7dcfaf47a81bc5bf7eb |
push id | 22827 |
push user | rcampbell@mozilla.com |
push date | Sun, 03 Jun 2012 20:41:58 +0000 |
treeherder | mozilla-central@0e4f8e1a141b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | zpao |
bugs | 742047 |
milestone | 15.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/browser/components/sessionstore/src/SessionStorage.jsm @@ -0,0 +1,109 @@ +/* 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 EXPORTED_SYMBOLS = ["SessionStorage"]; + +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "SessionStore", + "resource:///modules/sessionstore/SessionStore.jsm"); + +let SessionStorage = { + /** + * Updates all sessionStorage "super cookies" + * @param aTabData + * The data object for a specific tab + * @param aHistory + * That tab's session history + * @param aDocShell + * That tab's docshell (containing the sessionStorage) + * @param aFullData + * always return privacy sensitive data (use with care) + * @param aIsPinned + * the tab is pinned and should be treated differently for privacy + */ + serialize: function ssto_serialize(aTabData, aHistory, aDocShell, aFullData, + aIsPinned) { + let storageData = {}; + let hasContent = false; + + for (let i = 0; i < aHistory.count; i++) { + let uri; + try { + uri = aHistory.getEntryAtIndex(i, false).URI; + } + catch (ex) { + // Chances are that this is getEntryAtIndex throwing, as seen in bug 669196. + // We've already asserted in _collectTabData, so we won't show that again. + continue; + } + // sessionStorage is saved per origin (cf. nsDocShell::GetSessionStorageForURI) + let domain = uri.spec; + try { + if (uri.host) + domain = uri.prePath; + } + catch (ex) { /* this throws for host-less URIs (such as about: or jar:) */ } + if (storageData[domain] || + !(aFullData || SessionStore.checkPrivacyLevel(uri.schemeIs("https"), aIsPinned))) + continue; + + let storage, storageItemCount = 0; + try { + var principal = Services.scriptSecurityManager.getCodebasePrincipal(uri); + + // Using getSessionStorageForPrincipal instead of getSessionStorageForURI + // just to be able to pass aCreate = false, that avoids creation of the + // sessionStorage object for the page earlier than the page really + // requires it. It was causing problems while accessing a storage when + // a page later changed its domain. + storage = aDocShell.getSessionStorageForPrincipal(principal, "", false); + if (storage) + storageItemCount = storage.length; + } + catch (ex) { /* sessionStorage might throw if it's turned off, see bug 458954 */ } + if (storageItemCount == 0) + continue; + + let data = storageData[domain] = {}; + for (let j = 0; j < storageItemCount; j++) { + try { + let key = storage.key(j); + let item = storage.getItem(key); + data[key] = item; + } + catch (ex) { /* this currently throws for secured items (cf. bug 442048) */ } + } + hasContent = true; + } + + if (hasContent) + aTabData.storage = storageData; + }, + + /** + * restores all sessionStorage "super cookies" + * @param aStorageData + * Storage data to be restored + * @param aDocShell + * A tab's docshell (containing the sessionStorage) + */ + deserialize: function ssto_deserialize(aStorageData, aDocShell) { + for (let url in aStorageData) { + let uri = Services.io.newURI(url, null, null); + let storage = aDocShell.getSessionStorageForURI(uri, ""); + for (let key in aStorageData[url]) { + try { + storage.setItem(key, aStorageData[url][key]); + } + catch (ex) { Cu.reportError(ex); } // throws e.g. for URIs that can't have sessionStorage + } + } + } +}; + +Object.freeze(SessionStorage);
--- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -81,16 +81,18 @@ Cu.import("resource:///modules/Telemetry Cu.import("resource://gre/modules/TelemetryStopwatch.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager", "resource:///modules/devtools/scratchpad-manager.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DocumentUtils", "resource:///modules/sessionstore/DocumentUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage", + "resource:///modules/sessionstore/SessionStorage.jsm"); #ifdef MOZ_CRASHREPORTER XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter", "@mozilla.org/xre/app-info;1", "nsICrashReporter"); #endif function debug(aMsg) { aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n"); @@ -195,16 +197,20 @@ let SessionStore = { }, persistTabAttribute: function ss_persistTabAttribute(aName) { SessionStoreInternal.persistTabAttribute(aName); }, restoreLastSession: function ss_restoreLastSession() { SessionStoreInternal.restoreLastSession(); + }, + + checkPrivacyLevel: function ss_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) { + return SessionStoreInternal.checkPrivacyLevel(aIsHTTPS, aUseDefaultPref); } }; // Freeze the SessionStore object. We don't want anyone to modify it. Object.freeze(SessionStore); let SessionStoreInternal = { QueryInterface: XPCOMUtils.generateQI([ @@ -1983,18 +1989,18 @@ let SessionStoreInternal = { } if (aTab.__SS_extdata) tabData.extData = aTab.__SS_extdata; else if (tabData.extData) delete tabData.extData; if (history && browser.docShell instanceof Ci.nsIDocShell) - this._serializeSessionStorage(tabData, history, browser.docShell, aFullData, - aTab.pinned); + SessionStorage.serialize(tabData, history, browser.docShell, aFullData, + aTab.pinned); return tabData; }, /** * Get an object that is a serialized representation of a History entry * Used for data storage * @param aEntry @@ -2049,17 +2055,17 @@ let SessionStoreInternal = { var x = {}, y = {}; aEntry.getScrollPosition(x, y); if (x.value != 0 || y.value != 0) entry.scroll = x.value + "," + y.value; try { var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata"); if (aEntry.postData && (aFullData || prefPostdata && - this._checkPrivacyLevel(aEntry.URI.schemeIs("https"), aIsPinned))) { + this.checkPrivacyLevel(aEntry.URI.schemeIs("https"), aIsPinned))) { aEntry.postData.QueryInterface(Ci.nsISeekableStream). seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); var stream = Cc["@mozilla.org/binaryinputstream;1"]. createInstance(Ci.nsIBinaryInputStream); stream.setInputStream(aEntry.postData); var postBytes = stream.readByteArray(stream.available()); var postdata = String.fromCharCode.apply(null, postBytes); if (aFullData || prefPostdata == -1 || @@ -2131,88 +2137,16 @@ let SessionStoreInternal = { if (children.length) entry.children = children; } return entry; }, /** - * Updates all sessionStorage "super cookies" - * @param aTabData - * The data object for a specific tab - * @param aHistory - * That tab's session history - * @param aDocShell - * That tab's docshell (containing the sessionStorage) - * @param aFullData - * always return privacy sensitive data (use with care) - * @param aIsPinned - * the tab is pinned and should be treated differently for privacy - */ - _serializeSessionStorage: - function ssi_serializeSessionStorage(aTabData, aHistory, aDocShell, aFullData, aIsPinned) { - let storageData = {}; - let hasContent = false; - - for (let i = 0; i < aHistory.count; i++) { - let uri; - try { - uri = aHistory.getEntryAtIndex(i, false).URI; - } - catch (ex) { - // Chances are that this is getEntryAtIndex throwing, as seen in bug 669196. - // We've already asserted in _collectTabData, so we won't show that again. - continue; - } - // sessionStorage is saved per origin (cf. nsDocShell::GetSessionStorageForURI) - let domain = uri.spec; - try { - if (uri.host) - domain = uri.prePath; - } - catch (ex) { /* this throws for host-less URIs (such as about: or jar:) */ } - if (storageData[domain] || - !(aFullData || this._checkPrivacyLevel(uri.schemeIs("https"), aIsPinned))) - continue; - - let storage, storageItemCount = 0; - try { - var principal = Services.scriptSecurityManager.getCodebasePrincipal(uri); - - // Using getSessionStorageForPrincipal instead of getSessionStorageForURI - // just to be able to pass aCreate = false, that avoids creation of the - // sessionStorage object for the page earlier than the page really - // requires it. It was causing problems while accessing a storage when - // a page later changed its domain. - storage = aDocShell.getSessionStorageForPrincipal(principal, "", false); - if (storage) - storageItemCount = storage.length; - } - catch (ex) { /* sessionStorage might throw if it's turned off, see bug 458954 */ } - if (storageItemCount == 0) - continue; - - let data = storageData[domain] = {}; - for (let j = 0; j < storageItemCount; j++) { - try { - let key = storage.key(j); - let item = storage.getItem(key); - data[key] = item; - } - catch (ex) { /* XXXzeniko this currently throws for secured items (cf. bug 442048) */ } - } - hasContent = true; - } - - if (hasContent) - aTabData.storage = storageData; - }, - - /** * go through all tabs and store the current scroll positions * and innerHTML content of WYSIWYG editors * @param aWindow * Window reference */ _updateTextAndScrollData: function ssi_updateTextAndScrollData(aWindow) { var browsers = aWindow.gBrowser.browsers; this._windows[aWindow.__SSi].tabs.forEach(function (tabData, i) { @@ -2290,17 +2224,17 @@ let SessionStoreInternal = { if (aData.children && aData.children[i]) this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i], aData.children[i], aUpdateFormData, aFullData, aIsPinned); } var isHTTPS = this._getURIFromString((aContent.parent || aContent). document.location.href).schemeIs("https"); let isAboutSR = aContent.top.document.location.href == "about:sessionrestore"; - if (aFullData || this._checkPrivacyLevel(isHTTPS, aIsPinned) || isAboutSR) { + if (aFullData || this.checkPrivacyLevel(isHTTPS, aIsPinned) || isAboutSR) { if (aFullData || aUpdateFormData) { let formData = DocumentUtils.getFormData(aContent.document); // We want to avoid saving data for about:sessionrestore as a string. // Since it's stored in the form as stringified JSON, stringifying further // causes an explosion of escape characters. cf. bug 467409 if (formData && isAboutSR) { formData.id["sessionData"] = JSON.parse(formData.id["sessionData"]); @@ -2412,17 +2346,17 @@ let SessionStoreInternal = { * aCheckPrivacy */ _extractHostsForCookiesFromHostScheme: function ssi_extractHostsForCookiesFromHostScheme(aHost, aScheme, aHosts, aCheckPrivacy, aIsPinned) { // host and scheme may not be set (for about: urls for example), in which // case testing scheme will be sufficient. if (/https?/.test(aScheme) && !aHosts[aHost] && (!aCheckPrivacy || - this._checkPrivacyLevel(aScheme == "https", aIsPinned))) { + this.checkPrivacyLevel(aScheme == "https", aIsPinned))) { // By setting this to true or false, we can determine when looking at // the host in _updateCookies if we should check for privacy. aHosts[aHost] = aIsPinned; } else if (aScheme == "file") { aHosts[aHost] = true; } }, @@ -2483,18 +2417,18 @@ let SessionStoreInternal = { } catch (ex) { debug("getCookiesFromHost failed. Host: " + host); } while (list && list.hasMoreElements()) { var cookie = list.getNext().QueryInterface(Ci.nsICookie2); // window._hosts will only have hosts with the right privacy rules, // so there is no need to do anything special with this call to - // _checkPrivacyLevel. - if (cookie.isSession && _this._checkPrivacyLevel(cookie.isSecure, isPinned)) { + // checkPrivacyLevel. + if (cookie.isSession && _this.checkPrivacyLevel(cookie.isSecure, isPinned)) { // use the cookie's host, path, and name as keys into a hash, // to make sure we serialize each cookie only once if (!(cookie.host in jscookies && cookie.path in jscookies[cookie.host] && cookie.name in jscookies[cookie.host][cookie.path])) { var jscookie = { "host": cookie.host, "value": cookie.value }; // only add attributes with non-default values (saving a few bits) if (cookie.path) jscookie.path = cookie.path; @@ -3122,17 +3056,17 @@ let SessionStoreInternal = { browser.docShell["allow" + aCapability] = disallow.indexOf(aCapability) == -1; }); for (let name in this.xulAttributes) tab.removeAttribute(name); for (let name in tabData.attributes) tab.setAttribute(name, tabData.attributes[name]); if (tabData.storage && browser.docShell instanceof Ci.nsIDocShell) - this._deserializeSessionStorage(tabData.storage, browser.docShell); + SessionStorage.deserialize(tabData.storage, browser.docShell); // notify the tabbrowser that the tab chrome has been restored var event = aWindow.document.createEvent("Events"); event.initEvent("SSTabRestoring", true, false); tab.dispatchEvent(event); // Restore the history in the next tab aWindow.setTimeout(function(){ @@ -3422,36 +3356,16 @@ let SessionStoreInternal = { childDocIdents), i); } } return shEntry; }, /** - * restores all sessionStorage "super cookies" - * @param aStorageData - * Storage data to be restored - * @param aDocShell - * A tab's docshell (containing the sessionStorage) - */ - _deserializeSessionStorage: function ssi_deserializeSessionStorage(aStorageData, aDocShell) { - for (let url in aStorageData) { - let uri = this._getURIFromString(url); - let storage = aDocShell.getSessionStorageForURI(uri, ""); - for (let key in aStorageData[url]) { - try { - storage.setItem(key, aStorageData[url][key]); - } - catch (ex) { Cu.reportError(ex); } // throws e.g. for URIs that can't have sessionStorage - } - } - }, - - /** * Restore properties to a loaded document */ restoreDocument: function ssi_restoreDocument(aWindow, aBrowser, aEvent) { // wait for the top frame to be loaded completely if (!aEvent || !aEvent.originalTarget || !aEvent.originalTarget.defaultView || aEvent.originalTarget.defaultView != aEvent.originalTarget.defaultView.top) { return; } @@ -3924,17 +3838,17 @@ let SessionStoreInternal = { * don't save sensitive data if the user doesn't want to * (distinguishes between encrypted and non-encrypted sites) * @param aIsHTTPS * Bool is encrypted * @param aUseDefaultPref * don't do normal check for deferred * @returns bool */ - _checkPrivacyLevel: function ssi_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) { + checkPrivacyLevel: function ssi_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) { let pref = "sessionstore.privacy_level"; // If we're in the process of quitting and we're not autoresuming the session // then we should treat it as a deferred session. We have a different privacy // pref for that case. if (!aUseDefaultPref && this._loadState == STATE_QUITTING && !this._doResumeSession()) pref = "sessionstore.privacy_level_deferred"; return this._prefBranch.getIntPref(pref) < (aIsHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL); },