Bug 894595 - part 3 - Use asynchronous data collection for delayed save state calls; r=yoric

This commit is contained in:
Tim Taubert 2013-09-17 12:37:23 +02:00
Родитель 8a7788105c
Коммит 71ffe6f194
3 изменённых файлов: 97 добавлений и 3 удалений

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

@ -860,6 +860,8 @@ pref("browser.sessionstore.restore_pinned_tabs_on_demand", false);
pref("browser.sessionstore.upgradeBackup.latestBuildID", "");
// End-users should not run sessionstore in debug mode
pref("browser.sessionstore.debug", false);
// Enable asynchronous data collection by default.
pref("browser.sessionstore.async", true);
// allow META refresh by default
pref("accessibility.blockautorefresh", false);

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

@ -151,7 +151,7 @@ let SessionSaverInternal = {
delay = Math.max(this._lastSaveTime + gInterval - Date.now(), delay, 0);
// Schedule a state save.
this._timeoutID = setTimeout(() => this._saveState(), delay);
this._timeoutID = setTimeout(() => this._saveStateAsync(), delay);
},
/**
@ -186,8 +186,7 @@ let SessionSaverInternal = {
* 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.
// Cancel any pending timeouts.
this.cancel();
stopWatchStart("COLLECT_DATA_MS", "COLLECT_DATA_LONGEST_OP_MS");
@ -246,6 +245,33 @@ let SessionSaverInternal = {
this._writeState(state);
},
/**
* Saves the current session state. Collects data asynchronously and calls
* _saveState() to collect data again (with a cache hit rate of hopefully
* 100%) and write to disk afterwards.
*/
_saveStateAsync: function () {
// Allow scheduling delayed saves again.
this._timeoutID = null;
// Check whether asynchronous data collection is disabled.
if (!Services.prefs.getBoolPref("browser.sessionstore.async")) {
this._saveState();
return;
}
// Update the last save time to make sure we wait at least another interval
// length until we call _saveStateAsync() again.
this.updateLastSaveTime();
// Save state synchronously after all tab caches have been filled. The data
// for the tab caches is collected asynchronously. We will reuse this
// cached data if the tab hasn't been invalidated in the meantime. In that
// case we will just fall back to synchronous data collection for single
// tabs.
SessionStore.fillTabCachesAsynchronously().then(() => this._saveState());
},
/**
* Write the given state object to disk.
*/

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

@ -277,6 +277,10 @@ this.SessionStore = {
return SessionStoreInternal.getCurrentState(aUpdateAll);
},
fillTabCachesAsynchronously: function () {
return SessionStoreInternal.fillTabCachesAsynchronously();
},
/**
* Backstage pass to implementation details, used for testing purpose.
* Controlled by preference "browser.sessionstore.testmode".
@ -1883,6 +1887,68 @@ let SessionStoreInternal = {
return [true, canOverwriteTabs];
},
/* ........ Async Data Collection .............. */
/**
* Kicks off asynchronous data collection for all tabs that do not have any
* cached data. The returned promise will only notify that the tab collection
* has been finished without resolving to any data. The tab collection for a
* a few or all tabs might have failed or timed out. By calling
* fillTabCachesAsynchronously() and waiting for the promise to be resolved
* before calling getCurrentState(), callers ensure that most of the data
* should have been collected asynchronously, without blocking the main
* thread.
*
* @return {Promise} the promise that is fulfilled when the tab data is ready
*/
fillTabCachesAsynchronously: function () {
let countdown = 0;
let deferred = Promise.defer();
let activeWindow = this._getMostRecentBrowserWindow();
// The callback that will be called when a promise has been resolved
// successfully, i.e. the tab data has been collected.
function done() {
if (--countdown === 0) {
deferred.resolve();
}
}
// The callback that will be called when a promise is rejected, i.e. we
// we couldn't collect the tab data because of a script error or a timeout.
function fail(reason) {
debug("Failed collecting tab data asynchronously: " + reason);
done();
}
this._forEachBrowserWindow(win => {
if (!this._isWindowLoaded(win)) {
// Bail out if the window hasn't even loaded, yet.
return;
}
if (!DirtyWindows.has(win) && win != activeWindow) {
// Bail out if the window is not dirty and inactive.
return;
}
for (let tab of win.gBrowser.tabs) {
if (!TabStateCache.has(tab)) {
countdown++;
TabState.collect(tab).then(done, fail);
}
}
});
// If no dirty tabs were found, return a resolved
// promise because there is nothing to do here.
if (countdown == 0) {
return Promise.resolve();
}
return deferred.promise;
},
/* ........ Saving Functionality .............. */
/**