/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; this.EXPORTED_SYMBOLS = ["TabStateFlusher"]; const Cu = Components.utils; Cu.import("resource://gre/modules/Promise.jsm", this); /** * A module that enables async flushes. Updates from frame scripts are * throttled to be sent only once per second. If an action wants a tab's latest * state without waiting for a second then it can request an async flush and * wait until the frame scripts reported back. At this point the parent has the * latest data and the action can continue. */ this.TabStateFlusher = Object.freeze({ /** * Requests an async flush for the given browser. Returns a promise that will * resolve when we heard back from the content process and the parent has * all the latest data. */ flush(browser) { return TabStateFlusherInternal.flush(browser); }, /** * Requests an async flush for all browsers of a given window. Returns a Promise * that will resolve when we've heard back from all browsers. */ flushWindow(window) { return TabStateFlusherInternal.flushWindow(window); }, /** * Resolves the flush request with the given flush ID. * * @param browser () * The browser for which the flush is being resolved. * @param flushID (int) * The ID of the flush that was sent to the browser. * @param success (bool, optional) * Whether or not the flush succeeded. * @param message (string, optional) * An error message that will be sent to the Console in the * event that a flush failed. */ resolve(browser, flushID, success=true, message="") { TabStateFlusherInternal.resolve(browser, flushID, success, message); }, /** * Resolves all active flush requests for a given browser. This should be * used when the content process crashed or the final update message was * seen. In those cases we can't guarantee to ever hear back from the frame * script so we just resolve all requests instead of discarding them. * * @param browser () * The browser for which all flushes are being resolved. * @param success (bool, optional) * Whether or not the flushes succeeded. * @param message (string, optional) * An error message that will be sent to the Console in the * event that the flushes failed. */ resolveAll(browser, success=true, message="") { TabStateFlusherInternal.resolveAll(browser, success, message); } }); var TabStateFlusherInternal = { // Stores the last request ID. _lastRequestID: 0, // A map storing all active requests per browser. _requests: new WeakMap(), /** * Requests an async flush for the given browser. Returns a promise that will * resolve when we heard back from the content process and the parent has * all the latest data. */ flush(browser) { let id = ++this._lastRequestID; let mm = browser.messageManager; mm.sendAsyncMessage("SessionStore:flush", {id}); // Retrieve active requests for given browser. let permanentKey = browser.permanentKey; let perBrowserRequests = this._requests.get(permanentKey) || new Map(); return new Promise(resolve => { // Store resolve() so that we can resolve the promise later. perBrowserRequests.set(id, resolve); // Update the flush requests stored per browser. this._requests.set(permanentKey, perBrowserRequests); }); }, /** * Requests an async flush for all browsers of a given window. Returns a Promise * that will resolve when we've heard back from all browsers. */ flushWindow(window) { let browsers = window.gBrowser.browsers; let promises = browsers.map((browser) => this.flush(browser)); return Promise.all(promises); }, /** * Resolves the flush request with the given flush ID. * * @param browser () * The browser for which the flush is being resolved. * @param flushID (int) * The ID of the flush that was sent to the browser. * @param success (bool, optional) * Whether or not the flush succeeded. * @param message (string, optional) * An error message that will be sent to the Console in the * event that a flush failed. */ resolve(browser, flushID, success=true, message="") { // Nothing to do if there are no pending flushes for the given browser. if (!this._requests.has(browser.permanentKey)) { return; } // Retrieve active requests for given browser. let perBrowserRequests = this._requests.get(browser.permanentKey); if (!perBrowserRequests.has(flushID)) { return; } if (!success) { Cu.reportError("Failed to flush browser: " + message); } // Resolve the request with the given id. let resolve = perBrowserRequests.get(flushID); perBrowserRequests.delete(flushID); resolve(success); }, /** * Resolves all active flush requests for a given browser. This should be * used when the content process crashed or the final update message was * seen. In those cases we can't guarantee to ever hear back from the frame * script so we just resolve all requests instead of discarding them. * * @param browser () * The browser for which all flushes are being resolved. * @param success (bool, optional) * Whether or not the flushes succeeded. * @param message (string, optional) * An error message that will be sent to the Console in the * event that the flushes failed. */ resolveAll(browser, success=true, message="") { // Nothing to do if there are no pending flushes for the given browser. if (!this._requests.has(browser.permanentKey)) { return; } // Retrieve active requests for given browser. let perBrowserRequests = this._requests.get(browser.permanentKey); if (!success) { Cu.reportError("Failed to flush browser: " + message); } // Resolve all requests. for (let resolve of perBrowserRequests.values()) { resolve(success); } // Clear active requests. perBrowserRequests.clear(); } };