author | Will Wang <wiwang@mozilla.com> |
Wed, 13 Sep 2017 21:06:06 +0800 | |
changeset 380707 | 197a9e821ce8a565b1ec104b77a28b0de30b90fa |
parent 380706 | cfcce8492eb2bcc3fdf2bdd8c75c01b1836028ad |
child 380708 | 20ed0d0bd4b8412acf613ed586f7b17fa24ece2d |
push id | 32492 |
push user | archaeopteryx@coole-files.de |
push date | Wed, 13 Sep 2017 21:59:20 +0000 |
treeherder | mozilla-central@8645a74bbbd0 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mikedeboer |
bugs | 1365970 |
milestone | 57.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
browser/components/sessionstore/content/content-sessionStore.js | file | annotate | diff | comparison | revisions |
--- a/browser/components/sessionstore/content/content-sessionStore.js +++ b/browser/components/sessionstore/content/content-sessionStore.js @@ -46,16 +46,18 @@ var gCurrentEpoch = 0; // A bound to the size of data to store for DOM Storage. const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit"; // This pref controls whether or not we send updates to the parent on a timeout // or not, and should only be used for tests or debugging. const TIMEOUT_DISABLED_PREF = "browser.sessionstore.debug.no_auto_updates"; +const PREF_INTERVAL = "browser.sessionstore.interval"; + const kNoIndex = Number.MAX_SAFE_INTEGER; const kLastIndex = Number.MAX_SAFE_INTEGER - 1; /** * A function that will recursively call |cb| to collected data for all * non-dynamic frames in the current frame/docShell tree. */ function mapFrameTree(callback) { @@ -728,29 +730,46 @@ var MessageQueue = { /** * The delay (in ms) used to delay sending changes after data has been * invalidated. */ BATCH_DELAY_MS: 1000, /** + * The minimum idle period (in ms) we need for sending data to chrome process. + */ + NEEDED_IDLE_PERIOD_MS: 5, + + /** + * Timeout for waiting an idle period to send data. We will set this from + * the pref "browser.sessionstore.interval". + */ + _timeoutWaitIdlePeriodMs: null, + + /** * The current timeout ID, null if there is no queue data. We use timeouts * to damp a flood of data changes and send lots of changes as one batch. */ _timeout: null, /** * Whether or not sending batched messages on a timer is disabled. This should * only be used for debugging or testing. If you need to access this value, * you should probably use the timeoutDisabled getter. */ _timeoutDisabled: false, /** + * The idle callback ID referencing an active idle callback. When no idle + * callback is pending, this is null. + * */ + _idleCallbackID: null, + + /** * True if batched messages are not being fired on a timer. This should only * ever be true when debugging or during tests. */ get timeoutDisabled() { return this._timeoutDisabled; }, /** @@ -766,28 +785,58 @@ var MessageQueue = { } return val; }, init() { this.timeoutDisabled = Services.prefs.getBoolPref(TIMEOUT_DISABLED_PREF); + this._timeoutWaitIdlePeriodMs = + Services.prefs.getIntPref(PREF_INTERVAL); Services.prefs.addObserver(TIMEOUT_DISABLED_PREF, this); + Services.prefs.addObserver(PREF_INTERVAL, this); }, uninit() { Services.prefs.removeObserver(TIMEOUT_DISABLED_PREF, this); + Services.prefs.removeObserver(PREF_INTERVAL, this); + this.cleanupTimers(); + }, + + /** + * Cleanup pending idle callback and timer. + */ + cleanupTimers() { + if (this._idleCallbackID) { + content.cancelIdleCallback(this._idleCallbackID); + this._idleCallbackID = null; + } + if (this._timeout) { + clearTimeout(this._timeout); + this._timeout = null; + } }, observe(subject, topic, data) { - if (topic == "nsPref:changed" && data == TIMEOUT_DISABLED_PREF) { - this.timeoutDisabled = - Services.prefs.getBoolPref(TIMEOUT_DISABLED_PREF); + if (topic == "nsPref:changed") { + switch (data) { + case TIMEOUT_DISABLED_PREF: + this.timeoutDisabled = + Services.prefs.getBoolPref(TIMEOUT_DISABLED_PREF); + break; + case PREF_INTERVAL: + this._timeoutWaitIdlePeriodMs = + Services.prefs.getIntPref(PREF_INTERVAL); + break; + default: + debug("received unknown message '" + data + "'"); + break; + } } }, /** * Pushes a given |value| onto the queue. The given |key| represents the type * of data that is stored and can override data that has been queued before * but has not been sent to the parent process, yet. * @@ -798,39 +847,58 @@ var MessageQueue = { * process. */ push(key, fn) { this._data.set(key, fn); if (!this._timeout && !this._timeoutDisabled) { // Wait a little before sending the message to batch multiple changes. this._timeout = setTimeoutWithTarget( - () => this.send(), this.BATCH_DELAY_MS, tabEventTarget); + () => this.sendWhenIdle(), this.BATCH_DELAY_MS, tabEventTarget); } }, /** + * Sends queued data when the remaining idle time is enough or waiting too + * long; otherwise, request an idle time again. If the |deadline| is not + * given, this function is going to schedule the first request. + * + * @param deadline (object) + * An IdleDeadline object passed by requestIdleCallback(). + */ + sendWhenIdle(deadline) { + if (deadline) { + if (deadline.didTimeout || deadline.timeRemaining() > MessageQueue.NEEDED_IDLE_PERIOD_MS) { + MessageQueue.send(); + return; + } + } else if (MessageQueue._idleCallbackID) { + // Bail out if there's a pending run. + return; + } + MessageQueue._idleCallbackID = + content.requestIdleCallback(MessageQueue.sendWhenIdle, {timeout: MessageQueue._timeoutWaitIdlePeriodMs}); + }, + + /** * Sends queued data to the chrome process. * * @param options (object) * {flushID: 123} to specify that this is a flush * {isFinal: true} to signal this is the final message sent on unload */ send(options = {}) { // Looks like we have been called off a timeout after the tab has been // closed. The docShell is gone now and we can just return here as there // is nothing to do. if (!docShell) { return; } - if (this._timeout) { - clearTimeout(this._timeout); - this._timeout = null; - } + this.cleanupTimers(); let flushID = (options && options.flushID) || 0; let histID = "FX_SESSION_RESTORE_CONTENT_COLLECT_DATA_MS"; let data = {}; for (let [key, func] of this._data) { if (key != "isPrivate") { TelemetryStopwatch.startKeyed(histID, key);