Bug 600545 - getBrowserState() (and saved session state) incorrect with app tabs and browser.sessionstore.resume_from_crash = false [r=zpao, a=blocking2.0:final]

--HG--
extra : rebase_source : 34f6380284ae51f6afb5fae7f6fd94a2990ad2ae
This commit is contained in:
Michael Kraft 2010-10-14 12:36:00 -07:00
Родитель b1181d9b4f
Коммит aa9ab421ab
3 изменённых файлов: 208 добавлений и 16 удалений

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

@ -21,7 +21,7 @@
* Contributor(s):
* Dietrich Ayala <dietrich@mozilla.com>
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
* Michael Kraft <morac99-firefox@yahoo.com>
* Michael Kraft <morac99-firefox2@yahoo.com>
* Paul OShannessy <paul@oshannessy.com>
* Nils Maier <maierman@web.de>
*
@ -217,6 +217,16 @@ SessionStoreService.prototype = {
// Whether we've been initialized
_initialized: false,
// The original "sessionstore.resume_session_once" preference value before it
// was modified by saveState. saveState will set the
// "sessionstore.resume_session_once" to true when the
// the "sessionstore.resume_from_crash" preference is false (crash recovery
// is disabled) so that pinned tabs will be restored in the case of a
// crash. This variable is used to restore the original value so the
// previous session is not always restored when
// "sessionstore.resume_from_crash" is true.
_resume_session_once_on_shutdown: null,
/* ........ Public Getters .............. */
get canRestoreLastSession() {
@ -443,6 +453,15 @@ SessionStoreService.prototype = {
this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
this._clearingOnShutdown = false;
}
else if (this._resume_session_once_on_shutdown != null) {
// if the sessionstore.resume_session_once preference was changed by
// saveState because crash recovery is disabled then restore the
// preference back to the value it was prior to that. This will prevent
// SessionStore from always restoring the session when crash recovery is
// disabled.
this._prefBranch.setBoolPref("sessionstore.resume_session_once",
this._resume_session_once_on_shutdown);
}
this._loadState = STATE_QUITTING; // just to be sure
this._uninit();
break;
@ -550,6 +569,12 @@ SessionStoreService.prototype = {
break;
case "sessionstore.resume_from_crash":
this._resume_from_crash = this._prefBranch.getBoolPref("sessionstore.resume_from_crash");
// restore original resume_session_once preference if set in saveState
if (this._resume_session_once_on_shutdown != null) {
this._prefBranch.setBoolPref("sessionstore.resume_session_once",
this._resume_session_once_on_shutdown);
this._resume_session_once_on_shutdown = null;
}
// either create the file with crash recovery information or remove it
// (when _loadState is not STATE_RUNNING, that file is used for session resuming instead)
if (!this._resume_from_crash)
@ -1336,19 +1361,14 @@ SessionStoreService.prototype = {
* Store all session data for a window
* @param aWindow
* Window reference
* @param aPinnedOnly
* Bool collect pinned tabs only
*/
_saveWindowHistory: function sss_saveWindowHistory(aWindow, aPinnedOnly) {
_saveWindowHistory: function sss_saveWindowHistory(aWindow) {
var tabbrowser = aWindow.gBrowser;
var tabs = tabbrowser.tabs;
var tabsData = this._windows[aWindow.__SSi].tabs = [];
for (var i = 0; i < tabs.length; i++) {
if (aPinnedOnly && !tabs[i].pinned)
break;
for (var i = 0; i < tabs.length; i++)
tabsData.push(this._collectTabData(tabs[i]));
}
this._windows[aWindow.__SSi].selected = tabbrowser.mTabBox.selectedIndex + 1;
},
@ -2011,7 +2031,7 @@ SessionStoreService.prototype = {
if (!this._isWindowLoaded(aWindow)) // window data is still in _statesToRestore
return;
if (aUpdateAll || this._dirtyWindows[aWindow.__SSi] || aWindow == activeWindow) {
this._collectWindowData(aWindow, aPinnedOnly);
this._collectWindowData(aWindow);
}
else { // always update the window features (whose change alone never triggers a save operation)
this._updateWindowFeatures(aWindow);
@ -2063,8 +2083,15 @@ SessionStoreService.prototype = {
#endif
if (aPinnedOnly) {
// perform a deep copy so that existing session variables are not changed.
total = JSON.parse(this._toJSONString(total));
total = total.filter(function (win) {
win.tabs = win.tabs.filter(function (tab) tab.pinned);
// remove closed tabs
win._closedTabs = [];
// correct selected tab index if it was stripped out
if (win.selected > win.tabs.length)
win.selected = 1;
return win.tabs.length > 0;
});
if (total.length == 0)
@ -2077,8 +2104,9 @@ SessionStoreService.prototype = {
this.activeWindowSSiCache = activeWindow.__SSi || "";
}
ix = windows.indexOf(this.activeWindowSSiCache);
// We don't want to restore focus to a minimized window.
if (ix != -1 && total[ix].sizemode == "minimized")
// We don't want to restore focus to a minimized window or a window which had all its
// tabs stripped out (doesn't exist).
if (ix != -1 && total[ix] && total[ix].sizemode == "minimized")
ix = -1;
return { windows: total, selectedWindow: ix + 1, _closedWindows: lastClosedWindowsCopy };
@ -2104,12 +2132,12 @@ SessionStoreService.prototype = {
return { windows: total };
},
_collectWindowData: function sss_collectWindowData(aWindow, aPinnedOnly) {
_collectWindowData: function sss_collectWindowData(aWindow) {
if (!this._isWindowLoaded(aWindow))
return;
// update the internal state data for this window
this._saveWindowHistory(aWindow, aPinnedOnly);
this._saveWindowHistory(aWindow);
this._updateTextAndScrollData(aWindow);
this._updateCookieHosts(aWindow);
this._updateWindowFeatures(aWindow);
@ -3016,8 +3044,18 @@ SessionStoreService.prototype = {
if (!oState)
return;
if (pinnedOnly)
this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
if (pinnedOnly) {
// Save original resume_session_once preference for when quiting browser,
// otherwise session will be restored next time browser starts and we
// only want it to be restored in the case of a crash.
if (this._resume_session_once_on_shutdown == null) {
this._resume_session_once_on_shutdown =
this._prefBranch.getBoolPref("sessionstore.resume_session_once");
this._prefBranch.setBoolPref("sessionstore.resume_session_once", true);
// flush the preference file so preference will be saved in case of a crash
Services.prefs.savePrefFile(null);
}
}
oState.session = {
state: this._loadState == STATE_RUNNING ? STATE_RUNNING_STR : STATE_STOPPED_STR,

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

@ -20,7 +20,7 @@
#
# Contributor(s):
# Simon Bünzli <zeniko@gmail.com>
# Michael Kraft <morac99-firefox@yahoo.com>
# Michael Kraft <morac99-firefox2@yahoo.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
@ -118,6 +118,7 @@ _BROWSER_TEST_FILES = \
browser_580512.js \
browser_586147.js \
browser_586068-cascaded_restore.js \
browser_600545.js \
$(NULL)
ifneq ($(OS_ARCH),Darwin)

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

@ -0,0 +1,153 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is sessionstore test code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Kraft <morac99-firefox2@yahoo.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
let stateBackup = ss.getBrowserState();
function test() {
/** Test for Bug 600545 **/
waitForExplicitFinish();
testBug600545();
}
function testBug600545() {
// Set the pref to false to cause non-app tabs to be stripped out on a save
Services.prefs.setBoolPref("browser.sessionstore.resume_from_crash", false);
// Need to wait for SessionStore's saveState function to be called
// so that non-pinned tabs will be stripped from non-active window
function waitForSaveState(aSaveStateCallback) {
let topic = "sessionstore-state-write";
Services.obs.addObserver(function() {
Services.obs.removeObserver(arguments.callee, topic, false);
executeSoon(aSaveStateCallback);
}, topic, false);
};
// Need to wait for all tabs to be restored before reading browser state
function waitForBrowserState(aState, aSetStateCallback) {
let locationChanges = 0;
let tabsRestored = getStateTabCount(aState);
// Used to determine when tabs have been restored
let progressListener = {
onLocationChange: function (aBrowser) {
if (++locationChanges == tabsRestored) {
// Remove the progress listener from this window, it will be removed from
// theWin when that window is closed (in setBrowserState).
window.gBrowser.removeTabsProgressListener(this);
executeSoon(aSetStateCallback);
}
}
}
// We also want to catch the 2nd window, so we need to observe domwindowopened
function windowObserver(aSubject, aTopic, aData) {
let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
if (aTopic == "domwindowopened") {
theWin.addEventListener("load", function() {
theWin.removeEventListener("load", arguments.callee, false);
Services.ww.unregisterNotification(windowObserver);
theWin.gBrowser.addTabsProgressListener(progressListener);
}, false);
}
}
Services.ww.registerNotification(windowObserver);
window.gBrowser.addTabsProgressListener(progressListener);
ss.setBrowserState(JSON.stringify(aState));
}
// This tests the following use case:
// When multiple windows are open and browser.sessionstore.resume_from_crash
// preference is false, tab session data for non-active window is stripped for
// non-pinned tabs. This occurs after "sessionstore-state-write" fires which
// will only fire in this case if there is at least one pinned tab.
let state = { windows: [
{
tabs: [
{ entries: [{ url: "http://example.org#0" }], pinned:true },
{ entries: [{ url: "http://example.com#1" }] },
{ entries: [{ url: "http://example.com#2" }] },
],
selected: 2
},
{
tabs: [
{ entries: [{ url: "http://example.com#3" }] },
{ entries: [{ url: "http://example.com#4" }] },
{ entries: [{ url: "http://example.com#5" }] },
{ entries: [{ url: "http://example.com#6" }] }
],
selected: 3
}
] };
waitForBrowserState(state, function() {
waitForSaveState(function () {
let expectedNumberOfTabs = getStateTabCount(state);
let retrievedState = JSON.parse(ss.getBrowserState());
let actualNumberOfTabs = getStateTabCount(retrievedState);
is(actualNumberOfTabs, expectedNumberOfTabs,
"Number of tabs in retreived session data, matches number of tabs set.");
done();
});
});
}
function done() {
// Reset the pref
try {
Services.prefs.clearUserPref("browser.sessionstore.resume_from_crash");
} catch (e) {}
ss.setBrowserState(stateBackup);
executeSoon(finish);
}
// Count up the number of tabs in the state data
function getStateTabCount(aState) {
let tabCount = 0;
for (let i in aState.windows)
tabCount += aState.windows[i].tabs.length;
return tabCount;
}