Bug 395488 - Session restore restores blank windows if Firefox was shut down too quickly (re-land, with bug 462973 patch, r=dietrich)

This commit is contained in:
zeniko@gmail.com 2009-03-06 09:58:06 -08:00
Родитель 1bc2cfd0ca
Коммит cb1f693c36
1 изменённых файлов: 84 добавлений и 22 удалений

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

@ -152,6 +152,9 @@ SessionStoreService.prototype = {
// not-"dirty" windows usually don't need to have their data updated
_dirtyWindows: {},
// collection of session states yet to be restored
_statesToRestore: {},
// counts the number of crashes since the last clean start
_recentCrashes: 0,
@ -232,10 +235,14 @@ SessionStoreService.prototype = {
this._recentCrashes = (this._initialState.session &&
this._initialState.session.recentCrashes || 0) + 1;
if (this._needsRestorePage(this._initialState, this._recentCrashes))
if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
// replace the crashed session with a restore-page-only session
this._initialState =
{ windows: [{ tabs: [{ entries: [{ url: "about:sessionrestore" }] }] }] };
let pageData = {
url: "about:sessionrestore",
formdata: { "#sessionData": iniString }
};
this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] };
}
}
// make sure that at least the first window doesn't have anything hidden
@ -505,10 +512,6 @@ SessionStoreService.prototype = {
this._loadState = STATE_RUNNING;
this._lastSaveTime = Date.now();
// don't save during the first ten seconds
// (until most of the pages have been restored)
this.saveStateDelayed(aWindow, 10000);
// restore a crashed session resp. resume the last session if requested
if (this._initialState) {
// make sure that the restored tabs are first in the window
@ -516,14 +519,25 @@ SessionStoreService.prototype = {
this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
this.restoreWindow(aWindow, this._initialState, this._isCmdLineEmpty(aWindow));
delete this._initialState;
// mark ourselves as running
this.saveState(true);
}
else {
// Nothing to restore, notify observers things are complete.
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
observerService.notifyObservers(null, NOTIFY_WINDOWS_RESTORED, "");
// the next delayed save request should execute immediately
this._lastSaveTime -= this._interval;
}
}
// this window was opened by _openWindowWithState
else if (!this._isWindowLoaded(aWindow)) {
let followUp = this._statesToRestore[aWindow.__SS_restoreID].windows.length == 1;
this.restoreWindow(aWindow, this._statesToRestore[aWindow.__SS_restoreID], true, followUp);
}
var tabbrowser = aWindow.getBrowser();
var tabpanels = tabbrowser.mPanelContainer;
@ -546,6 +560,16 @@ SessionStoreService.prototype = {
* Window reference
*/
onClose: function sss_onClose(aWindow) {
// this window was about to be restored - conserve its original data, if any
let isFullyLoaded = this._isWindowLoaded(aWindow);
if (!isFullyLoaded) {
if (!aWindow.__SSi)
aWindow.__SSi = "window" + Date.now();
this._window[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID];
delete this._statesToRestore[aWindow.__SS_restoreID];
delete aWindow.__SS_restoreID;
}
// ignore windows not tracked by SessionStore
if (!aWindow.__SSi || !this._windows[aWindow.__SSi]) {
return;
@ -562,21 +586,22 @@ SessionStoreService.prototype = {
tabbrowser.removeEventListener("TabClose", this, true);
tabbrowser.removeEventListener("TabSelect", this, true);
let winData = this._windows[aWindow.__SSi];
if (this._loadState == STATE_RUNNING) { // window not closed during a regular shut-down
// update all window data for a last time
this._collectWindowData(aWindow);
// preserve this window's data (in case it was the last navigator:browser)
var winData = this._windows[aWindow.__SSi];
winData.title = aWindow.content.document.title;
// if this is a popup window, append it to what we've already got (cf. bug 368677)
if (!this._lastClosedWindows || !winData.isPopup)
this._lastClosedWindows = [winData];
else
this._lastClosedWindows.push(winData);
this._updateCookies(this._lastClosedWindows);
if (isFullyLoaded) {
winData.title = aWindow.content.document.title;
this._updateCookies(this._lastClosedWindows);
}
// clear this window from the list
delete this._windows[aWindow.__SSi];
@ -590,7 +615,7 @@ SessionStoreService.prototype = {
}
// cache the window state until the window is completely gone
aWindow.__SS_dyingCache = this._windows[aWindow.__SSi] || winData;
aWindow.__SS_dyingCache = winData;
delete aWindow.__SSi;
},
@ -757,9 +782,16 @@ SessionStoreService.prototype = {
},
setBrowserState: function sss_setBrowserState(aState) {
try {
var state = this._safeEval("(" + aState + ")");
}
catch (ex) { /* invalid state object - don't restore anything */ }
if (!state || !state.windows)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
var window = this._getMostRecentBrowserWindow();
if (!window) {
this._openWindowWithState("(" + aState + ")");
this._openWindowWithState(state);
return;
}
@ -771,7 +803,7 @@ SessionStoreService.prototype = {
});
// restore to the given state
this.restoreWindow(window, "(" + aState + ")", true);
this.restoreWindow(window, state, true);
},
getWindowState: function sss_getWindowState(aWindow) {
@ -1475,6 +1507,8 @@ SessionStoreService.prototype = {
if (this._loadState == STATE_RUNNING) {
// update the data for all windows with activities since the last save operation
this._forEachBrowserWindow(function(aWindow) {
if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
return;
if (aUpdateAll || this._dirtyWindows[aWindow.__SSi] || aWindow == activeWindow) {
this._collectWindowData(aWindow);
}
@ -1497,6 +1531,15 @@ SessionStoreService.prototype = {
}
this._updateCookies(total);
// collect the data for all windows yet to be restored
for (ix in this._statesToRestore) {
for each (let winData in this._statesToRestore[ix].windows) {
total.push(winData);
if (!winData.isPopup)
nonPopupCount++;
}
}
#ifndef XP_MACOSX
// if no non-popup browser window remains open, return the state of the last closed window(s)
if (nonPopupCount == 0 && this._lastClosedWindows) {
@ -1521,6 +1564,9 @@ SessionStoreService.prototype = {
* @returns string
*/
_getWindowState: function sss_getWindowState(aWindow) {
if (!this._isWindowLoaded(aWindow))
return this._statesToRestore[aWindow.__SS_restoreID];
if (this._loadState == STATE_RUNNING) {
this._collectWindowData(aWindow);
}
@ -1532,6 +1578,9 @@ SessionStoreService.prototype = {
},
_collectWindowData: function sss_collectWindowData(aWindow) {
if (!this._isWindowLoaded(aWindow))
return;
// update the internal state data for this window
this._saveWindowHistory(aWindow);
this._updateTextAndScrollData(aWindow);
@ -1719,6 +1768,12 @@ SessionStoreService.prototype = {
tabbrowser.selectedTab = aTabs[0];
}
if (!this._isWindowLoaded(aWindow)) {
// from now on, the data will come from the actual window
delete this._statesToRestore[aWindow.__SS_restoreID];
delete aWindow.__SS_restoreID;
}
// helper hash for ensuring unique frame IDs
var idMap = { used: {} };
this.restoreHistory(aWindow, aTabs, aTabData, idMap);
@ -2318,13 +2373,10 @@ SessionStoreService.prototype = {
openWindow(null, this._prefBranch.getCharPref("chromeURL"), "_blank",
"chrome,dialog=no,all", argString);
window.__SS_state = aState;
var _this = this;
window.addEventListener("load", function(aEvent) {
aEvent.currentTarget.removeEventListener("load", arguments.callee, true);
_this.restoreWindow(aEvent.currentTarget, aEvent.currentTarget.__SS_state, true, true);
delete aEvent.currentTarget.__SS_state;
}, true);
do {
var ID = "window" + Math.random();
} while (ID in this._statesToRestore);
this._statesToRestore[(window.__SS_restoreID = ID)] = aState;
return window;
},
@ -2510,6 +2562,16 @@ SessionStoreService.prototype = {
}
},
/**
* @param aWindow
* Window reference
* @returns whether this window's data is still cached in _statesToRestore
* because it's not fully loaded yet
*/
_isWindowLoaded: function sss_isWindowLoaded(aWindow) {
return !aWindow.__SS_restoreID;
},
/* ........ Storage API .............. */
/**