mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 11:55:49 +00:00
merge fx-team to mozilla-central
This commit is contained in:
commit
972d9d6e50
@ -308,7 +308,8 @@ WebContentConverterRegistrar.prototype = {
|
||||
function WCCR_checkAndGetURI(aURIString, aContentWindow)
|
||||
{
|
||||
try {
|
||||
var uri = this._makeURI(aURIString);
|
||||
let baseURI = aContentWindow.document.baseURIObject;
|
||||
var uri = this._makeURI(aURIString, null, baseURI);
|
||||
} catch (ex) {
|
||||
// not supposed to throw according to spec
|
||||
return;
|
||||
|
@ -46,9 +46,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=402788
|
||||
is(false, testRegisterHandler(true, "foo", "http://mochi.test:8888/", "Foo handler"), "a protocol handler uri should contain %s");
|
||||
is(false, testRegisterHandler(false, "application/rss+xml", "http://mochi.test:8888/", "Foo handler"), "a content handler uri should contain %s");
|
||||
|
||||
// the spec says we should not throw here, but it probably needs to be changed
|
||||
is(false, testRegisterHandler(true, "foo", "foo/%s", "Foo handler"), "a protocol handler uri should be valid");
|
||||
is(false, testRegisterHandler(false, "application/rss+xml", "foo/%s", "Foo handler"), "a content handler uri should be valid");
|
||||
// the spec explicitly allows relative urls to be passed
|
||||
is(true, testRegisterHandler(true, "foo", "foo/%s", "Foo handler"), "a protocol handler uri should be valid");
|
||||
is(true, testRegisterHandler(false, "application/rss+xml", "foo/%s", "Foo handler"), "a content handler uri should be valid");
|
||||
|
||||
// we should only accept to register when the handler has the same host as the current page (bug 402287)
|
||||
is(false, testRegisterHandler(true, "foo", "http://remotehost:8888/%s", "Foo handler"), "registering a foo protocol handler with a different host should not work");
|
||||
|
249
browser/components/sessionstore/src/SessionCookies.jsm
Normal file
249
browser/components/sessionstore/src/SessionCookies.jsm
Normal file
@ -0,0 +1,249 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SessionCookies"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision.
|
||||
const MAX_EXPIRY = Math.pow(2, 62);
|
||||
|
||||
|
||||
/**
|
||||
* The external API implemented by the SessionCookies module.
|
||||
*/
|
||||
this.SessionCookies = Object.freeze({
|
||||
getCookiesForHost: function (host) {
|
||||
return SessionCookiesInternal.getCookiesForHost(host);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The internal API.
|
||||
*/
|
||||
let SessionCookiesInternal = {
|
||||
/**
|
||||
* Stores whether we're initialized, yet.
|
||||
*/
|
||||
_initialized: false,
|
||||
|
||||
/**
|
||||
* Returns the list of active session cookies for a given host.
|
||||
*/
|
||||
getCookiesForHost: function (host) {
|
||||
this._ensureInitialized();
|
||||
return CookieStore.getCookiesForHost(host);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles observers notifications that are sent whenever cookies are added,
|
||||
* changed, or removed. Ensures that the storage is updated accordingly.
|
||||
*/
|
||||
observe: function (subject, topic, data) {
|
||||
switch (data) {
|
||||
case "added":
|
||||
case "changed":
|
||||
this._updateCookie(subject);
|
||||
break;
|
||||
case "deleted":
|
||||
this._removeCookie(subject);
|
||||
break;
|
||||
case "cleared":
|
||||
CookieStore.clear();
|
||||
break;
|
||||
case "batch-deleted":
|
||||
this._removeCookies(subject);
|
||||
break;
|
||||
case "reload":
|
||||
CookieStore.clear();
|
||||
this._reloadCookies();
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unhandled cookie-changed notification.");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If called for the first time in a session, iterates all cookies in the
|
||||
* cookies service and puts them into the store if they're session cookies.
|
||||
*/
|
||||
_ensureInitialized: function () {
|
||||
if (!this._initialized) {
|
||||
this._reloadCookies();
|
||||
this._initialized = true;
|
||||
Services.obs.addObserver(this, "cookie-changed", false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates or adds a given cookie to the store.
|
||||
*/
|
||||
_updateCookie: function (cookie) {
|
||||
cookie.QueryInterface(Ci.nsICookie2);
|
||||
|
||||
if (cookie.isSession) {
|
||||
CookieStore.set(cookie);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a given cookie from the store.
|
||||
*/
|
||||
_removeCookie: function (cookie) {
|
||||
cookie.QueryInterface(Ci.nsICookie2);
|
||||
|
||||
if (cookie.isSession) {
|
||||
CookieStore.delete(cookie);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a given list of cookies from the store.
|
||||
*/
|
||||
_removeCookies: function (cookies) {
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
this._removeCookie(cookies.queryElementAt(i, Ci.nsICookie2));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterates all cookies in the cookies service and puts them into the store
|
||||
* if they're session cookies.
|
||||
*/
|
||||
_reloadCookies: function () {
|
||||
let iter = Services.cookies.enumerator;
|
||||
while (iter.hasMoreElements()) {
|
||||
this._updateCookie(iter.getNext());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The internal cookie storage that keeps track of every active session cookie.
|
||||
* These are stored using maps per host, path, and cookie name.
|
||||
*/
|
||||
let CookieStore = {
|
||||
/**
|
||||
* The internal structure holding all known cookies.
|
||||
*
|
||||
* Host =>
|
||||
* Path =>
|
||||
* Name => {path: "/", name: "sessionid", secure: true}
|
||||
*
|
||||
* Maps are used for storage but the data structure is equivalent to this:
|
||||
*
|
||||
* this._hosts = {
|
||||
* "www.mozilla.org": {
|
||||
* "/": {
|
||||
* "username": {name: "username", value: "my_name_is", etc...},
|
||||
* "sessionid": {name: "sessionid", value: "1fdb3a", etc...}
|
||||
* }
|
||||
* },
|
||||
* "tbpl.mozilla.org": {
|
||||
* "/path": {
|
||||
* "cookiename": {name: "cookiename", value: "value", etc...}
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
_hosts: new Map(),
|
||||
|
||||
/**
|
||||
* Returns the list of stored session cookies for a given host.
|
||||
*
|
||||
* @param host
|
||||
* A string containing the host name we want to get cookies for.
|
||||
*/
|
||||
getCookiesForHost: function (host) {
|
||||
if (!this._hosts.has(host)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let cookies = [];
|
||||
|
||||
for (let pathToNamesMap of this._hosts.get(host).values()) {
|
||||
cookies = cookies.concat([cookie for (cookie of pathToNamesMap.values())]);
|
||||
}
|
||||
|
||||
return cookies;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stores a given cookie.
|
||||
*
|
||||
* @param cookie
|
||||
* The nsICookie2 object to add to the storage.
|
||||
*/
|
||||
set: function (cookie) {
|
||||
let jscookie = {host: cookie.host, value: cookie.value};
|
||||
|
||||
// Only add properties with non-default values to save a few bytes.
|
||||
if (cookie.path) {
|
||||
jscookie.path = cookie.path;
|
||||
}
|
||||
|
||||
if (cookie.name) {
|
||||
jscookie.name = cookie.name;
|
||||
}
|
||||
|
||||
if (cookie.isSecure) {
|
||||
jscookie.secure = true;
|
||||
}
|
||||
|
||||
if (cookie.isHttpOnly) {
|
||||
jscookie.httponly = true;
|
||||
}
|
||||
|
||||
if (cookie.expiry < MAX_EXPIRY) {
|
||||
jscookie.expiry = cookie.expiry;
|
||||
}
|
||||
|
||||
this._ensureMap(cookie).set(cookie.name, jscookie);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a given cookie.
|
||||
*
|
||||
* @param cookie
|
||||
* The nsICookie2 object to be removed from storage.
|
||||
*/
|
||||
delete: function (cookie) {
|
||||
this._ensureMap(cookie).delete(cookie.name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all cookies.
|
||||
*/
|
||||
clear: function () {
|
||||
this._hosts.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates all maps necessary to store a given cookie.
|
||||
*
|
||||
* @param cookie
|
||||
* The nsICookie2 object to create maps for.
|
||||
*
|
||||
* @return The newly created Map instance mapping cookie names to
|
||||
* internal jscookies, in the given path of the given host.
|
||||
*/
|
||||
_ensureMap: function (cookie) {
|
||||
if (!this._hosts.has(cookie.host)) {
|
||||
this._hosts.set(cookie.host, new Map());
|
||||
}
|
||||
|
||||
let pathToNamesMap = this._hosts.get(cookie.host);
|
||||
|
||||
if (!pathToNamesMap.has(cookie.path)) {
|
||||
pathToNamesMap.set(cookie.path, new Map());
|
||||
}
|
||||
|
||||
return pathToNamesMap.get(cookie.path);
|
||||
}
|
||||
};
|
283
browser/components/sessionstore/src/SessionSaver.jsm
Normal file
283
browser/components/sessionstore/src/SessionSaver.jsm
Normal file
@ -0,0 +1,283 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SessionSaver"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/Timer.jsm", this);
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", this);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile",
|
||||
"resource:///modules/sessionstore/_SessionFile.jsm");
|
||||
|
||||
// Minimal interval between two save operations (in milliseconds).
|
||||
XPCOMUtils.defineLazyGetter(this, "gInterval", function () {
|
||||
const PREF = "browser.sessionstore.interval";
|
||||
|
||||
// Observer that updates the cached value when the preference changes.
|
||||
Services.prefs.addObserver(PREF, () => {
|
||||
this.gInterval = Services.prefs.getIntPref(PREF);
|
||||
|
||||
// Cancel any pending runs and call runDelayed() with
|
||||
// zero to apply the newly configured interval.
|
||||
SessionSaverInternal.cancel();
|
||||
SessionSaverInternal.runDelayed(0);
|
||||
}, false);
|
||||
|
||||
return Services.prefs.getIntPref(PREF);
|
||||
});
|
||||
|
||||
// Wrap a string as a nsISupports.
|
||||
function createSupportsString(data) {
|
||||
let string = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
string.data = data;
|
||||
return string;
|
||||
}
|
||||
|
||||
// Notify observers about a given topic with a given subject.
|
||||
function notify(subject, topic) {
|
||||
Services.obs.notifyObservers(subject, topic, "");
|
||||
}
|
||||
|
||||
// TelemetryStopwatch helper functions.
|
||||
function stopWatch(method) {
|
||||
return function (...histograms) {
|
||||
for (let hist of histograms) {
|
||||
TelemetryStopwatch[method]("FX_SESSION_RESTORE_" + hist);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let stopWatchStart = stopWatch("start");
|
||||
let stopWatchCancel = stopWatch("cancel");
|
||||
let stopWatchFinish = stopWatch("finish");
|
||||
|
||||
/**
|
||||
* The external API implemented by the SessionSaver module.
|
||||
*/
|
||||
this.SessionSaver = Object.freeze({
|
||||
/**
|
||||
* Immediately saves the current session to disk.
|
||||
*/
|
||||
run: function () {
|
||||
SessionSaverInternal.run();
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the current session to disk delayed by a given amount of time. Should
|
||||
* another delayed run be scheduled already, we will ignore the given delay
|
||||
* and state saving may occur a little earlier.
|
||||
*/
|
||||
runDelayed: function () {
|
||||
SessionSaverInternal.runDelayed();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the last save time to the current time. This will cause us to wait for
|
||||
* at least the configured interval when runDelayed() is called next.
|
||||
*/
|
||||
updateLastSaveTime: function () {
|
||||
SessionSaverInternal.updateLastSaveTime();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the last save time to zero. This will cause us to
|
||||
* immediately save the next time runDelayed() is called.
|
||||
*/
|
||||
clearLastSaveTime: function () {
|
||||
SessionSaverInternal.clearLastSaveTime();
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancels all pending session saves.
|
||||
*/
|
||||
cancel: function () {
|
||||
SessionSaverInternal.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The internal API.
|
||||
*/
|
||||
let SessionSaverInternal = {
|
||||
/**
|
||||
* The timeout ID referencing an active timer for a delayed save. When no
|
||||
* save is pending, this is null.
|
||||
*/
|
||||
_timeoutID: null,
|
||||
|
||||
/**
|
||||
* A timestamp that keeps track of when we saved the session last. We will
|
||||
* this to determine the correct interval between delayed saves to not deceed
|
||||
* the configured session write interval.
|
||||
*/
|
||||
_lastSaveTime: 0,
|
||||
|
||||
/**
|
||||
* Immediately saves the current session to disk.
|
||||
*/
|
||||
run: function () {
|
||||
this._saveState(true /* force-update all windows */);
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the current session to disk delayed by a given amount of time. Should
|
||||
* another delayed run be scheduled already, we will ignore the given delay
|
||||
* and state saving may occur a little earlier.
|
||||
*
|
||||
* @param delay (optional)
|
||||
* The minimum delay in milliseconds to wait for until we collect and
|
||||
* save the current session.
|
||||
*/
|
||||
runDelayed: function (delay = 2000) {
|
||||
// Bail out if there's a pending run.
|
||||
if (this._timeoutID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Interval until the next disk operation is allowed.
|
||||
delay = Math.max(this._lastSaveTime + gInterval - Date.now(), delay, 0);
|
||||
|
||||
// Schedule a state save.
|
||||
this._timeoutID = setTimeout(() => this._saveState(), delay);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the last save time to the current time. This will cause us to wait for
|
||||
* at least the configured interval when runDelayed() is called next.
|
||||
*/
|
||||
updateLastSaveTime: function () {
|
||||
this._lastSaveTime = Date.now();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the last save time to zero. This will cause us to
|
||||
* immediately save the next time runDelayed() is called.
|
||||
*/
|
||||
clearLastSaveTime: function () {
|
||||
this._lastSaveTime = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancels all pending session saves.
|
||||
*/
|
||||
cancel: function () {
|
||||
clearTimeout(this._timeoutID);
|
||||
this._timeoutID = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the current session state. Collects data and writes to disk.
|
||||
*
|
||||
* @param forceUpdateAllWindows (optional)
|
||||
* Forces us to recollect data for all windows and will bypass and
|
||||
* update the corresponding caches.
|
||||
*/
|
||||
_saveState: function (forceUpdateAllWindows = false) {
|
||||
// Cancel any pending timeouts or just clear
|
||||
// the timeout if this is why we've been called.
|
||||
this.cancel();
|
||||
|
||||
stopWatchStart("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
|
||||
|
||||
let state = SessionStore.getCurrentState(forceUpdateAllWindows);
|
||||
if (!state) {
|
||||
stopWatchCancel("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
|
||||
return;
|
||||
}
|
||||
|
||||
// Forget about private windows.
|
||||
for (let i = state.windows.length - 1; i >= 0; i--) {
|
||||
if (state.windows[i].isPrivate) {
|
||||
state.windows.splice(i, 1);
|
||||
if (state.selectedWindow >= i) {
|
||||
state.selectedWindow--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
// Don't save invalid states.
|
||||
// Looks like we currently have private windows, only.
|
||||
if (state.windows.length == 0) {
|
||||
stopWatchCancel("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Remove private windows from the list of closed windows.
|
||||
for (let i = state._closedWindows.length - 1; i >= 0; i--) {
|
||||
if (state._closedWindows[i].isPrivate) {
|
||||
state._closedWindows.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
// We want to restore closed windows that are marked with _shouldRestore.
|
||||
// We're doing this here because we want to control this only when saving
|
||||
// the file.
|
||||
while (state._closedWindows.length) {
|
||||
let i = state._closedWindows.length - 1;
|
||||
|
||||
if (!state._closedWindows[i]._shouldRestore) {
|
||||
// We only need to go until _shouldRestore
|
||||
// is falsy since we're going in reverse.
|
||||
break;
|
||||
}
|
||||
|
||||
delete state._closedWindows[i]._shouldRestore;
|
||||
state.windows.unshift(state._closedWindows.pop());
|
||||
}
|
||||
#endif
|
||||
|
||||
stopWatchFinish("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
|
||||
this._writeState(state);
|
||||
},
|
||||
|
||||
/**
|
||||
* Write the given state object to disk.
|
||||
*/
|
||||
_writeState: function (state) {
|
||||
stopWatchStart("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS");
|
||||
let data = JSON.stringify(state);
|
||||
stopWatchFinish("SERIALIZE_DATA_MS", "SERIALIZE_DATA_LONGEST_OP_MS");
|
||||
|
||||
// Give observers a chance to modify session data.
|
||||
data = this._notifyObserversBeforeStateWrite(data);
|
||||
|
||||
// Don't touch the file if an observer has deleted all state data.
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write (atomically) to a session file, using a tmp file. Once the session
|
||||
// file is successfully updated, save the time stamp of the last save and
|
||||
// notify the observers.
|
||||
_SessionFile.write(data).then(() => {
|
||||
this.updateLastSaveTime();
|
||||
notify(null, "sessionstore-state-write-complete");
|
||||
}, Cu.reportError);
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify sessionstore-state-write observer and give them a
|
||||
* chance to modify session data before we'll write it to disk.
|
||||
*/
|
||||
_notifyObserversBeforeStateWrite: function (data) {
|
||||
let stateString = createSupportsString(data);
|
||||
notify(stateString, "sessionstore-state-write");
|
||||
return stateString.data;
|
||||
}
|
||||
};
|
@ -118,8 +118,12 @@ XPCOMUtils.defineLazyModuleGetter(this, "ScratchpadManager",
|
||||
"resource:///modules/devtools/scratchpad-manager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DocumentUtils",
|
||||
"resource:///modules/sessionstore/DocumentUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver",
|
||||
"resource:///modules/sessionstore/SessionSaver.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
|
||||
"resource:///modules/sessionstore/SessionStorage.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionCookies",
|
||||
"resource:///modules/sessionstore/SessionCookies.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "_SessionFile",
|
||||
"resource:///modules/sessionstore/_SessionFile.jsm");
|
||||
|
||||
@ -261,6 +265,10 @@ this.SessionStore = {
|
||||
return SessionStoreInternal.checkPrivacyLevel(aIsHTTPS, aUseDefaultPref);
|
||||
},
|
||||
|
||||
getCurrentState: function (aUpdateAll) {
|
||||
return SessionStoreInternal.getCurrentState(aUpdateAll);
|
||||
},
|
||||
|
||||
/**
|
||||
* Backstage pass to implementation details, used for testing purpose.
|
||||
* Controlled by preference "browser.sessionstore.testmode".
|
||||
@ -286,9 +294,6 @@ let SessionStoreInternal = {
|
||||
// set default load state
|
||||
_loadState: STATE_STOPPED,
|
||||
|
||||
// initial state to restore after startup
|
||||
_initialState: null,
|
||||
|
||||
// During the initial restore and setBrowserState calls tracks the number of
|
||||
// windows yet to be restored
|
||||
_restoreCount: -1,
|
||||
@ -296,9 +301,6 @@ let SessionStoreInternal = {
|
||||
// whether a setBrowserState call is in progress
|
||||
_browserSetState: false,
|
||||
|
||||
// time in milliseconds (Date.now()) when the session was last written to file
|
||||
_lastSaveTime: 0,
|
||||
|
||||
// time in milliseconds when the session was started (saved across sessions),
|
||||
// defaults to now if no session was restored or timestamp doesn't exist
|
||||
_sessionStartTime: Date.now(),
|
||||
@ -389,11 +391,11 @@ let SessionStoreInternal = {
|
||||
// Wait until nsISessionStartup has finished reading the session data.
|
||||
gSessionStartup.onceInitialized.then(() => {
|
||||
// Parse session data and start restoring.
|
||||
this.initSession();
|
||||
let initialState = this.initSession();
|
||||
|
||||
// Start tracking the given (initial) browser window.
|
||||
if (!aWindow.closed) {
|
||||
this.onLoad(aWindow);
|
||||
this.onLoad(aWindow, initialState);
|
||||
}
|
||||
|
||||
// Let everyone know we're done.
|
||||
@ -402,74 +404,76 @@ let SessionStoreInternal = {
|
||||
},
|
||||
|
||||
initSession: function ssi_initSession() {
|
||||
let state;
|
||||
let ss = gSessionStartup;
|
||||
|
||||
try {
|
||||
if (ss.doRestore() ||
|
||||
ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION)
|
||||
this._initialState = ss.state;
|
||||
state = ss.state;
|
||||
}
|
||||
catch(ex) { dump(ex + "\n"); } // no state to restore, which is ok
|
||||
|
||||
if (this._initialState) {
|
||||
if (state) {
|
||||
try {
|
||||
// If we're doing a DEFERRED session, then we want to pull pinned tabs
|
||||
// out so they can be restored.
|
||||
if (ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION) {
|
||||
let [iniState, remainingState] = this._prepDataForDeferredRestore(this._initialState);
|
||||
let [iniState, remainingState] = this._prepDataForDeferredRestore(state);
|
||||
// If we have a iniState with windows, that means that we have windows
|
||||
// with app tabs to restore.
|
||||
if (iniState.windows.length)
|
||||
this._initialState = iniState;
|
||||
state = iniState;
|
||||
else
|
||||
this._initialState = null;
|
||||
state = null;
|
||||
if (remainingState.windows.length)
|
||||
this._lastSessionState = remainingState;
|
||||
}
|
||||
else {
|
||||
// Get the last deferred session in case the user still wants to
|
||||
// restore it
|
||||
this._lastSessionState = this._initialState.lastSessionState;
|
||||
this._lastSessionState = state.lastSessionState;
|
||||
|
||||
let lastSessionCrashed =
|
||||
this._initialState.session && this._initialState.session.state &&
|
||||
this._initialState.session.state == STATE_RUNNING_STR;
|
||||
state.session && state.session.state &&
|
||||
state.session.state == STATE_RUNNING_STR;
|
||||
if (lastSessionCrashed) {
|
||||
this._recentCrashes = (this._initialState.session &&
|
||||
this._initialState.session.recentCrashes || 0) + 1;
|
||||
this._recentCrashes = (state.session &&
|
||||
state.session.recentCrashes || 0) + 1;
|
||||
|
||||
if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
|
||||
if (this._needsRestorePage(state, this._recentCrashes)) {
|
||||
// replace the crashed session with a restore-page-only session
|
||||
let pageData = {
|
||||
url: "about:sessionrestore",
|
||||
formdata: {
|
||||
id: { "sessionData": this._initialState },
|
||||
id: { "sessionData": state },
|
||||
xpath: {}
|
||||
}
|
||||
};
|
||||
this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] };
|
||||
} else if (this._hasSingleTabWithURL(this._initialState.windows,
|
||||
state = { windows: [{ tabs: [{ entries: [pageData] }] }] };
|
||||
} else if (this._hasSingleTabWithURL(state.windows,
|
||||
"about:welcomeback")) {
|
||||
// On a single about:welcomeback URL that crashed, replace about:welcomeback
|
||||
// with about:sessionrestore, to make clear to the user that we crashed.
|
||||
this._initialState.windows[0].tabs[0].entries[0].url = "about:sessionrestore";
|
||||
state.windows[0].tabs[0].entries[0].url = "about:sessionrestore";
|
||||
}
|
||||
}
|
||||
|
||||
// Load the session start time from the previous state
|
||||
this._sessionStartTime = this._initialState.session &&
|
||||
this._initialState.session.startTime ||
|
||||
this._sessionStartTime = state.session &&
|
||||
state.session.startTime ||
|
||||
this._sessionStartTime;
|
||||
|
||||
// make sure that at least the first window doesn't have anything hidden
|
||||
delete this._initialState.windows[0].hidden;
|
||||
delete state.windows[0].hidden;
|
||||
// Since nothing is hidden in the first window, it cannot be a popup
|
||||
delete this._initialState.windows[0].isPopup;
|
||||
delete state.windows[0].isPopup;
|
||||
// We don't want to minimize and then open a window at startup.
|
||||
if (this._initialState.windows[0].sizemode == "minimized")
|
||||
this._initialState.windows[0].sizemode = "normal";
|
||||
if (state.windows[0].sizemode == "minimized")
|
||||
state.windows[0].sizemode = "normal";
|
||||
// clear any lastSessionWindowID attributes since those don't matter
|
||||
// during normal restore
|
||||
this._initialState.windows.forEach(function(aWindow) {
|
||||
state.windows.forEach(function(aWindow) {
|
||||
delete aWindow.__lastSessionWindowID;
|
||||
});
|
||||
}
|
||||
@ -484,8 +488,9 @@ let SessionStoreInternal = {
|
||||
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
|
||||
|
||||
this._performUpgradeBackup();
|
||||
|
||||
this._sessionInitialized = true;
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -526,13 +531,6 @@ let SessionStoreInternal = {
|
||||
gDebuggingEnabled = this._prefBranch.getBoolPref("sessionstore.debug");
|
||||
}, false);
|
||||
|
||||
// minimal interval between two save operations (in milliseconds)
|
||||
XPCOMUtils.defineLazyGetter(this, "_interval", function () {
|
||||
// used often, so caching/observing instead of fetching on-demand
|
||||
this._prefBranch.addObserver("sessionstore.interval", this, true);
|
||||
return this._prefBranch.getIntPref("sessionstore.interval");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_max_tabs_undo", function () {
|
||||
this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true);
|
||||
return this._prefBranch.getIntPref("sessionstore.max_tabs_undo");
|
||||
@ -555,17 +553,14 @@ let SessionStoreInternal = {
|
||||
|
||||
// save all data for session resuming
|
||||
if (this._sessionInitialized) {
|
||||
this.saveState(true);
|
||||
SessionSaver.run();
|
||||
}
|
||||
|
||||
// clear out priority queue in case it's still holding refs
|
||||
TabRestoreQueue.reset();
|
||||
|
||||
// Make sure to break our cycle with the save timer
|
||||
if (this._saveTimer) {
|
||||
this._saveTimer.cancel();
|
||||
this._saveTimer = null;
|
||||
}
|
||||
// Make sure to cancel pending saves.
|
||||
SessionSaver.cancel();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -700,8 +695,10 @@ let SessionStoreInternal = {
|
||||
* Set up event listeners for this window's tabs
|
||||
* @param aWindow
|
||||
* Window reference
|
||||
* @param aInitialState
|
||||
* The initial state to be loaded after startup (optional)
|
||||
*/
|
||||
onLoad: function ssi_onLoad(aWindow) {
|
||||
onLoad: function ssi_onLoad(aWindow, aInitialState = null) {
|
||||
// return if window has already been initialized
|
||||
if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi])
|
||||
return;
|
||||
@ -728,27 +725,26 @@ let SessionStoreInternal = {
|
||||
// perform additional initialization when the first window is loading
|
||||
if (this._loadState == STATE_STOPPED) {
|
||||
this._loadState = STATE_RUNNING;
|
||||
this._lastSaveTime = Date.now();
|
||||
SessionSaver.updateLastSaveTime();
|
||||
|
||||
// restore a crashed session resp. resume the last session if requested
|
||||
if (this._initialState) {
|
||||
if (aInitialState) {
|
||||
if (isPrivateWindow) {
|
||||
// We're starting with a single private window. Save the state we
|
||||
// actually wanted to restore so that we can do it later in case
|
||||
// the user opens another, non-private window.
|
||||
this._deferredInitialState = this._initialState;
|
||||
this._initialState = null;
|
||||
this._deferredInitialState = aInitialState;
|
||||
|
||||
// Nothing to restore now, notify observers things are complete.
|
||||
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
|
||||
} else {
|
||||
TelemetryTimestamps.add("sessionRestoreRestoring");
|
||||
// make sure that the restored tabs are first in the window
|
||||
this._initialState._firstTabs = true;
|
||||
this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
|
||||
this.restoreWindow(aWindow, this._initialState,
|
||||
this._isCmdLineEmpty(aWindow, this._initialState));
|
||||
this._initialState = null;
|
||||
aInitialState._firstTabs = true;
|
||||
this._restoreCount = aInitialState.windows ? aInitialState.windows.length : 0;
|
||||
|
||||
let overwrite = this._isCmdLineEmpty(aWindow, aInitialState);
|
||||
this.restoreWindow(aWindow, aInitialState, overwrite);
|
||||
|
||||
// _loadState changed from "stopped" to "running". Save the session's
|
||||
// load state immediately so that crashes happening during startup
|
||||
@ -760,8 +756,8 @@ let SessionStoreInternal = {
|
||||
// Nothing to restore, notify observers things are complete.
|
||||
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
|
||||
|
||||
// the next delayed save request should execute immediately
|
||||
this._lastSaveTime -= this._interval;
|
||||
// The next delayed save request should execute immediately.
|
||||
SessionSaver.clearLastSaveTime();
|
||||
}
|
||||
}
|
||||
// this window was opened by _openWindowWithState
|
||||
@ -1032,7 +1028,6 @@ let SessionStoreInternal = {
|
||||
* On purge of session history
|
||||
*/
|
||||
onPurgeSessionHistory: function ssi_onPurgeSessionHistory() {
|
||||
var _this = this;
|
||||
_SessionFile.wipe();
|
||||
// If the browser is shutting down, simply return after clearing the
|
||||
// session data on disk as this notification fires after the
|
||||
@ -1064,10 +1059,11 @@ let SessionStoreInternal = {
|
||||
this._closedWindows = [];
|
||||
// give the tabbrowsers a chance to clear their histories first
|
||||
var win = this._getMostRecentBrowserWindow();
|
||||
if (win)
|
||||
win.setTimeout(function() { _this.saveState(true); }, 0);
|
||||
else if (this._loadState == STATE_RUNNING)
|
||||
this.saveState(true);
|
||||
if (win) {
|
||||
win.setTimeout(() => SessionSaver.run(), 0);
|
||||
} else if (this._loadState == STATE_RUNNING) {
|
||||
SessionSaver.run();
|
||||
}
|
||||
|
||||
this._clearRestoringWindows();
|
||||
},
|
||||
@ -1124,8 +1120,10 @@ let SessionStoreInternal = {
|
||||
this._closedWindows[ix].title = selectedTab.entries[activeIndex].title;
|
||||
}
|
||||
}
|
||||
if (this._loadState == STATE_RUNNING)
|
||||
this.saveState(true);
|
||||
|
||||
if (this._loadState == STATE_RUNNING) {
|
||||
SessionSaver.run();
|
||||
}
|
||||
|
||||
this._clearRestoringWindows();
|
||||
},
|
||||
@ -1149,26 +1147,9 @@ let SessionStoreInternal = {
|
||||
this._max_windows_undo = this._prefBranch.getIntPref("sessionstore.max_windows_undo");
|
||||
this._capClosedWindows();
|
||||
break;
|
||||
case "sessionstore.interval":
|
||||
this._interval = this._prefBranch.getIntPref("sessionstore.interval");
|
||||
// reset timer and save
|
||||
if (this._saveTimer) {
|
||||
this._saveTimer.cancel();
|
||||
this._saveTimer = null;
|
||||
}
|
||||
this.saveStateDelayed(null, -1);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* On timer callback
|
||||
*/
|
||||
onTimerCallback: function ssi_onTimerCallback() {
|
||||
this._saveTimer = null;
|
||||
this.saveState();
|
||||
},
|
||||
|
||||
/**
|
||||
* set up listeners for a new tab
|
||||
* @param aWindow
|
||||
@ -1309,7 +1290,7 @@ let SessionStoreInternal = {
|
||||
|
||||
TabStateCache.delete(aBrowser);
|
||||
|
||||
this.saveStateDelayed(aWindow, 3000);
|
||||
this.saveStateDelayed(aWindow);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1371,7 +1352,12 @@ let SessionStoreInternal = {
|
||||
/* ........ nsISessionStore API .............. */
|
||||
|
||||
getBrowserState: function ssi_getBrowserState() {
|
||||
return this._toJSONString(this._getCurrentState());
|
||||
let state = this.getCurrentState();
|
||||
|
||||
// Don't include the last session state in getBrowserState().
|
||||
delete state.lastSessionState;
|
||||
|
||||
return this._toJSONString(state);
|
||||
},
|
||||
|
||||
setBrowserState: function ssi_setBrowserState(aState) {
|
||||
@ -2402,21 +2388,6 @@ let SessionStoreInternal = {
|
||||
* { tabs: [ ... ], etc. }
|
||||
*/
|
||||
_updateCookies: function ssi_updateCookies(aWindows) {
|
||||
function addCookieToHash(aHash, aHost, aPath, aName, aCookie) {
|
||||
// lazily build up a 3-dimensional hash, with
|
||||
// aHost, aPath, and aName as keys
|
||||
if (!aHash[aHost])
|
||||
aHash[aHost] = {};
|
||||
if (!aHash[aHost][aPath])
|
||||
aHash[aHost][aPath] = {};
|
||||
aHash[aHost][aPath][aName] = aCookie;
|
||||
}
|
||||
|
||||
var jscookies = {};
|
||||
var _this = this;
|
||||
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
|
||||
var MAX_EXPIRY = Math.pow(2, 62);
|
||||
|
||||
for (let window of aWindows) {
|
||||
window.cookies = [];
|
||||
|
||||
@ -2429,35 +2400,12 @@ let SessionStoreInternal = {
|
||||
}, this);
|
||||
|
||||
for (var [host, isPinned] in Iterator(hosts)) {
|
||||
let list;
|
||||
try {
|
||||
list = Services.cookies.getCookiesFromHost(host);
|
||||
}
|
||||
catch (ex) {
|
||||
debug("getCookiesFromHost failed. Host: " + host);
|
||||
}
|
||||
while (list && list.hasMoreElements()) {
|
||||
var cookie = list.getNext().QueryInterface(Ci.nsICookie2);
|
||||
for (let cookie of SessionCookies.getCookiesForHost(host)) {
|
||||
// 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)) {
|
||||
// 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;
|
||||
if (cookie.name) jscookie.name = cookie.name;
|
||||
if (cookie.isSecure) jscookie.secure = true;
|
||||
if (cookie.isHttpOnly) jscookie.httponly = true;
|
||||
if (cookie.expiry < MAX_EXPIRY) jscookie.expiry = cookie.expiry;
|
||||
|
||||
addCookieToHash(jscookies, cookie.host, cookie.path, cookie.name, jscookie);
|
||||
}
|
||||
window.cookies.push(jscookies[cookie.host][cookie.path][cookie.name]);
|
||||
if (this.checkPrivacyLevel(cookie.secure, isPinned)) {
|
||||
window.cookies.push(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2501,7 +2449,7 @@ let SessionStoreInternal = {
|
||||
* Bool update all windows
|
||||
* @returns object
|
||||
*/
|
||||
_getCurrentState: function ssi_getCurrentState(aUpdateAll) {
|
||||
getCurrentState: function (aUpdateAll) {
|
||||
this._handleClosedWindows();
|
||||
|
||||
var activeWindow = this._getMostRecentBrowserWindow();
|
||||
@ -2587,13 +2535,20 @@ let SessionStoreInternal = {
|
||||
// get open Scratchpad window states too
|
||||
var scratchpads = ScratchpadManager.getSessionState();
|
||||
|
||||
return {
|
||||
let state = {
|
||||
windows: total,
|
||||
selectedWindow: ix + 1,
|
||||
_closedWindows: lastClosedWindowsCopy,
|
||||
session: session,
|
||||
scratchpads: scratchpads
|
||||
};
|
||||
|
||||
// Persist the last session if we deferred restoring it
|
||||
if (this._lastSessionState) {
|
||||
state.lastSessionState = this._lastSessionState;
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -3649,146 +3604,22 @@ let SessionStoreInternal = {
|
||||
/* ........ Disk Access .............. */
|
||||
|
||||
/**
|
||||
* save state delayed by N ms
|
||||
* marks window as dirty (i.e. data update can't be skipped)
|
||||
* @param aWindow
|
||||
* Window reference
|
||||
* @param aDelay
|
||||
* Milliseconds to delay
|
||||
* Save the current session state to disk, after a delay.
|
||||
*
|
||||
* @param aWindow (optional)
|
||||
* Will mark the given window as dirty so that we will recollect its
|
||||
* data before we start writing.
|
||||
*/
|
||||
saveStateDelayed: function ssi_saveStateDelayed(aWindow = null, aDelay = 2000) {
|
||||
saveStateDelayed: function (aWindow = null) {
|
||||
if (aWindow) {
|
||||
DirtyWindows.add(aWindow);
|
||||
}
|
||||
|
||||
if (!this._saveTimer) {
|
||||
// interval until the next disk operation is allowed
|
||||
var minimalDelay = this._lastSaveTime + this._interval - Date.now();
|
||||
|
||||
// if we have to wait, set a timer, otherwise saveState directly
|
||||
aDelay = Math.max(minimalDelay, aDelay);
|
||||
if (aDelay > 0) {
|
||||
this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this._saveTimer.init(this, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
else {
|
||||
this.saveState();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* save state to disk
|
||||
* @param aUpdateAll
|
||||
* Bool update all windows
|
||||
*/
|
||||
saveState: function ssi_saveState(aUpdateAll) {
|
||||
// If crash recovery is disabled, we only want to resume with pinned tabs
|
||||
// if we crash.
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_DATA_MS");
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_DATA_LONGEST_OP_MS");
|
||||
|
||||
var oState = this._getCurrentState(aUpdateAll);
|
||||
if (!oState) {
|
||||
TelemetryStopwatch.cancel("FX_SESSION_RESTORE_COLLECT_DATA_MS");
|
||||
TelemetryStopwatch.cancel("FX_SESSION_RESTORE_COLLECT_DATA_LONGEST_OP_MS");
|
||||
return;
|
||||
}
|
||||
|
||||
// Forget about private windows.
|
||||
for (let i = oState.windows.length - 1; i >= 0; i--) {
|
||||
if (oState.windows[i].isPrivate) {
|
||||
oState.windows.splice(i, 1);
|
||||
if (oState.selectedWindow >= i) {
|
||||
oState.selectedWindow--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
// Don't save invalid states.
|
||||
// Looks like we currently have private windows, only.
|
||||
if (oState.windows.length == 0) {
|
||||
TelemetryStopwatch.cancel("FX_SESSION_RESTORE_COLLECT_DATA_MS");
|
||||
TelemetryStopwatch.cancel("FX_SESSION_RESTORE_COLLECT_DATA_LONGEST_OP_MS");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (let i = oState._closedWindows.length - 1; i >= 0; i--) {
|
||||
if (oState._closedWindows[i].isPrivate) {
|
||||
oState._closedWindows.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef XP_MACOSX
|
||||
// We want to restore closed windows that are marked with _shouldRestore.
|
||||
// We're doing this here because we want to control this only when saving
|
||||
// the file.
|
||||
while (oState._closedWindows.length) {
|
||||
let i = oState._closedWindows.length - 1;
|
||||
if (oState._closedWindows[i]._shouldRestore) {
|
||||
delete oState._closedWindows[i]._shouldRestore;
|
||||
oState.windows.unshift(oState._closedWindows.pop());
|
||||
}
|
||||
else {
|
||||
// We only need to go until we hit !needsRestore since we're going in reverse
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Persist the last session if we deferred restoring it
|
||||
if (this._lastSessionState)
|
||||
oState.lastSessionState = this._lastSessionState;
|
||||
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_DATA_MS");
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_COLLECT_DATA_LONGEST_OP_MS");
|
||||
|
||||
this._saveStateObject(oState);
|
||||
},
|
||||
|
||||
/**
|
||||
* write a state object to disk
|
||||
*/
|
||||
_saveStateObject: function ssi_saveStateObject(aStateObj) {
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_SERIALIZE_DATA_MS");
|
||||
TelemetryStopwatch.start("FX_SESSION_RESTORE_SERIALIZE_DATA_LONGEST_OP_MS");
|
||||
let data = this._toJSONString(aStateObj);
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_SERIALIZE_DATA_MS");
|
||||
TelemetryStopwatch.finish("FX_SESSION_RESTORE_SERIALIZE_DATA_LONGEST_OP_MS");
|
||||
|
||||
let stateString = this._createSupportsString(data);
|
||||
Services.obs.notifyObservers(stateString, "sessionstore-state-write", "");
|
||||
data = stateString.data;
|
||||
|
||||
// Don't touch the file if an observer has deleted all state data.
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Write (atomically) to a session file, using a tmp file.
|
||||
let promise = _SessionFile.write(data);
|
||||
|
||||
// Once the session file is successfully updated, save the time stamp of the
|
||||
// last save and notify the observers.
|
||||
promise = promise.then(() => {
|
||||
this._lastSaveTime = Date.now();
|
||||
Services.obs.notifyObservers(null, "sessionstore-state-write-complete",
|
||||
"");
|
||||
});
|
||||
SessionSaver.runDelayed();
|
||||
},
|
||||
|
||||
/* ........ Auxiliary Functions .............. */
|
||||
|
||||
// Wrap a string as a nsISupports
|
||||
_createSupportsString: function ssi_createSupportsString(aData) {
|
||||
let string = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
string.data = aData;
|
||||
return string;
|
||||
},
|
||||
|
||||
/**
|
||||
* call a callback for all currently opened browser windows
|
||||
* (might miss the most recent one)
|
||||
@ -4647,7 +4478,7 @@ let DyingWindowCache = {
|
||||
};
|
||||
|
||||
// A weak set of dirty windows. We use it to determine which windows we need to
|
||||
// recollect data for when _getCurrentState() is called.
|
||||
// recollect data for when getCurrentState() is called.
|
||||
let DirtyWindows = {
|
||||
_data: new WeakMap(),
|
||||
|
||||
|
@ -14,6 +14,7 @@ JS_MODULES_PATH = 'modules/sessionstore'
|
||||
|
||||
EXTRA_JS_MODULES = [
|
||||
'DocumentUtils.jsm',
|
||||
'SessionCookies.jsm',
|
||||
'SessionMigration.jsm',
|
||||
'SessionStorage.jsm',
|
||||
'SessionWorker.js',
|
||||
@ -22,6 +23,7 @@ EXTRA_JS_MODULES = [
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES += [
|
||||
'SessionSaver.jsm',
|
||||
'SessionStore.jsm',
|
||||
]
|
||||
|
||||
|
@ -343,13 +343,9 @@ HiddenBrowser.prototype = {
|
||||
},
|
||||
|
||||
resize: function (width, height) {
|
||||
if (this._browser) {
|
||||
this._browser.style.width = width + "px";
|
||||
this._browser.style.height = height + "px";
|
||||
} else {
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
}
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._applySize();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
@ -357,12 +353,20 @@ HiddenBrowser.prototype = {
|
||||
this._timer = clearTimer(this._timer);
|
||||
},
|
||||
|
||||
_applySize: function () {
|
||||
if (this._browser) {
|
||||
this._browser.style.width = this._width + "px";
|
||||
this._browser.style.height = this._height + "px";
|
||||
}
|
||||
},
|
||||
|
||||
_createBrowser: function () {
|
||||
HostFrame.get().then(aFrame => {
|
||||
let doc = aFrame.document;
|
||||
this._browser = doc.createElementNS(XUL_NS, "browser");
|
||||
this._browser.setAttribute("type", "content");
|
||||
this._browser.setAttribute("src", NEWTAB_URL);
|
||||
this._applySize();
|
||||
doc.getElementById("win").appendChild(this._browser);
|
||||
});
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user