Bug 965137 - Synchronously write session on application-background. r=mfinkle

This commit is contained in:
Brian Nicholson 2014-02-05 23:47:51 -08:00
parent e1357d14ef
commit d65b69f986

View File

@ -43,6 +43,7 @@ SessionStore.prototype = {
_lastSaveTime: 0, _lastSaveTime: 0,
_interval: 10000, _interval: 10000,
_maxTabsUndo: 1, _maxTabsUndo: 1,
_pendingWrite: 0,
init: function ss_init() { init: function ss_init() {
// Get file references // Get file references
@ -77,6 +78,7 @@ SessionStore.prototype = {
observerService.addObserver(this, "domwindowclosed", true); observerService.addObserver(this, "domwindowclosed", true);
observerService.addObserver(this, "browser:purge-session-history", true); observerService.addObserver(this, "browser:purge-session-history", true);
observerService.addObserver(this, "Session:Restore", true); observerService.addObserver(this, "Session:Restore", true);
observerService.addObserver(this, "application-background", true);
break; break;
case "final-ui-startup": case "final-ui-startup":
observerService.removeObserver(this, "final-ui-startup"); observerService.removeObserver(this, "final-ui-startup");
@ -102,7 +104,7 @@ SessionStore.prototype = {
if (this._loadState == STATE_RUNNING) { if (this._loadState == STATE_RUNNING) {
// Save the purged state immediately // Save the purged state immediately
this.saveStateNow(); this.saveState();
} }
Services.obs.notifyObservers(null, "sessionstore-state-purge-complete", ""); Services.obs.notifyObservers(null, "sessionstore-state-purge-complete", "");
@ -110,7 +112,9 @@ SessionStore.prototype = {
case "timer-callback": case "timer-callback":
// Timer call back for delayed saving // Timer call back for delayed saving
this._saveTimer = null; this._saveTimer = null;
this.saveState(); if (this._pendingWrite) {
this.saveState();
}
break; break;
case "Session:Restore": { case "Session:Restore": {
if (aData) { if (aData) {
@ -143,6 +147,13 @@ SessionStore.prototype = {
} }
break; break;
} }
case "application-background":
// We receive this notification when Android's onPause callback is
// executed. After onPause, the application may be terminated at any
// point without notice; therefore, we must synchronously write out any
// pending save state to ensure that this data does not get lost.
this.flushPendingState();
break;
} }
}, },
@ -306,7 +317,7 @@ SessionStore.prototype = {
delete aBrowser.__SS_data; delete aBrowser.__SS_data;
this._collectTabData(aWindow, aBrowser, data); this._collectTabData(aWindow, aBrowser, data);
this.saveStateNow(); this.saveState();
} }
this._updateCrashReportURL(aWindow); this._updateCrashReportURL(aWindow);
@ -342,6 +353,7 @@ SessionStore.prototype = {
// If we have to wait, set a timer, otherwise saveState directly // If we have to wait, set a timer, otherwise saveState directly
let delay = Math.max(minimalDelay, 2000); let delay = Math.max(minimalDelay, 2000);
if (delay > 0) { if (delay > 0) {
this._pendingWrite++;
this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); this._saveTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._saveTimer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT); this._saveTimer.init(this, delay, Ci.nsITimer.TYPE_ONE_SHOT);
} else { } else {
@ -350,16 +362,25 @@ SessionStore.prototype = {
} }
}, },
saveStateNow: function ss_saveStateNow() { saveState: function ss_saveState() {
this._pendingWrite++;
this._saveState(true);
},
// Immediately and synchronously writes any pending state to disk.
flushPendingState: function ss_flushPendingState() {
if (this._pendingWrite) {
this._saveState(false);
}
},
_saveState: function ss_saveState(aAsync) {
// Kill any queued timer and save immediately // Kill any queued timer and save immediately
if (this._saveTimer) { if (this._saveTimer) {
this._saveTimer.cancel(); this._saveTimer.cancel();
this._saveTimer = null; this._saveTimer = null;
} }
this.saveState();
},
saveState: function ss_saveState() {
let data = this._getCurrentState(); let data = this._getCurrentState();
let normalData = { windows: [] }; let normalData = { windows: [] };
let privateData = { windows: [] }; let privateData = { windows: [] };
@ -388,7 +409,7 @@ SessionStore.prototype = {
} }
// Write only non-private data to disk // Write only non-private data to disk
this._writeFile(this._sessionFile, JSON.stringify(normalData)); this._writeFile(this._sessionFile, JSON.stringify(normalData), aAsync);
// If we have private data, send it to Java; otherwise, send null to // If we have private data, send it to Java; otherwise, send null to
// indicate that there is no private data // indicate that there is no private data
@ -464,7 +485,7 @@ SessionStore.prototype = {
} }
}, },
_writeFile: function ss_writeFile(aFile, aData) { _writeFile: function ss_writeFile(aFile, aData, aAsync) {
let stateString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); let stateString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
stateString.data = aData; stateString.data = aData;
Services.obs.notifyObservers(stateString, "sessionstore-state-write", ""); Services.obs.notifyObservers(stateString, "sessionstore-state-write", "");
@ -473,11 +494,28 @@ SessionStore.prototype = {
if (!stateString.data) if (!stateString.data)
return; return;
// Asynchronously copy the data to the file. if (aAsync) {
let array = new TextEncoder().encode(aData); let array = new TextEncoder().encode(aData);
OS.File.writeAtomic(aFile.path, array, { tmpPath: aFile.path + ".tmp" }).then(function onSuccess() { let pendingWrite = this._pendingWrite;
Services.obs.notifyObservers(null, "sessionstore-state-write-complete", ""); OS.File.writeAtomic(aFile.path, array, { tmpPath: aFile.path + ".tmp" }).then(function onSuccess() {
}); // Make sure this._pendingWrite is the same value it was before we
// fired off the async write. If the count is different, another write
// is pending, so we shouldn't reset this._pendingWrite yet.
if (pendingWrite === this._pendingWrite)
this._pendingWrite = 0;
Services.obs.notifyObservers(null, "sessionstore-state-write-complete", "");
}.bind(this));
} else {
this._pendingWrite = 0;
let foStream = Cc["@mozilla.org/network/file-output-stream;1"].
createInstance(Ci.nsIFileOutputStream);
foStream.init(aFile, 0x02 | 0x08 | 0x20, 0666, 0);
let converter = Cc["@mozilla.org/intl/converter-output-stream;1"].
createInstance(Ci.nsIConverterOutputStream);
converter.init(foStream, "UTF-8", 0, 0);
converter.writeString(aData);
converter.close();
}
}, },
_updateCrashReportURL: function ss_updateCrashReportURL(aWindow) { _updateCrashReportURL: function ss_updateCrashReportURL(aWindow) {