TabStateFlusher.sys.mjs (5102B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * A module that enables async flushes. Updates from frame scripts are 7 * throttled to be sent only once per second. If an action wants a tab's latest 8 * state without waiting for a second then it can request an async flush and 9 * wait until the frame scripts reported back. At this point the parent has the 10 * latest data and the action can continue. 11 */ 12 export var TabStateFlusher = Object.freeze({ 13 /** 14 * Requests an async flush for the given browser. Returns a promise that will 15 * resolve when we heard back from the content process and the parent has 16 * all the latest data. 17 */ 18 flush(browser) { 19 return TabStateFlusherInternal.flush(browser); 20 }, 21 22 /** 23 * Requests an async flush for all browsers of a given window. Returns a Promise 24 * that will resolve when we've heard back from all browsers. 25 */ 26 flushWindow(window) { 27 return TabStateFlusherInternal.flushWindow(window); 28 }, 29 30 /** 31 * Resolves all active flush requests for a given browser. This should be 32 * used when the content process crashed or the final update message was 33 * seen. In those cases we can't guarantee to ever hear back from the frame 34 * script so we just resolve all requests instead of discarding them. 35 * 36 * @param browser (<xul:browser>) 37 * The browser for which all flushes are being resolved. 38 * @param success (bool, optional) 39 * Whether or not the flushes succeeded. 40 * @param message (string, optional) 41 * An error message that will be sent to the Console in the 42 * event that the flushes failed. 43 */ 44 resolveAll(browser, success = true, message = "") { 45 TabStateFlusherInternal.resolveAll(browser, success, message); 46 }, 47 }); 48 49 var TabStateFlusherInternal = { 50 // A map storing all active requests per browser. A request is a 51 // triple of a map containing all flush requests, a promise that 52 // resolve when a request for a browser is canceled, and the 53 // function to call to cancel a reqeust. 54 _requests: new WeakMap(), 55 56 initEntry(entry) { 57 entry.cancelPromise = new Promise(resolve => { 58 entry.cancel = resolve; 59 }).then(result => { 60 TabStateFlusherInternal.initEntry(entry); 61 return result; 62 }); 63 64 return entry; 65 }, 66 67 /** 68 * Requests an async flush for the given browser. Returns a promise that will 69 * resolve when we heard back from the content process and the parent has 70 * all the latest data. 71 */ 72 flush(browser) { 73 let nativePromise = Promise.resolve(); 74 if (browser && browser.frameLoader) { 75 /* 76 Request native listener to flush the tabState. 77 Resolves when flush is complete. 78 */ 79 nativePromise = browser.frameLoader.requestTabStateFlush(); 80 } 81 82 // Retrieve active requests for given browser. 83 let permanentKey = browser.permanentKey; 84 let request = this._requests.get(permanentKey); 85 if (!request) { 86 // If we don't have any requests for this browser, create a new 87 // entry for browser. 88 request = this.initEntry({}); 89 this._requests.set(permanentKey, request); 90 } 91 92 // It's fine to resolve the request immediately after the native promise 93 // resolves, since SessionStore will have processed all updates from this 94 // browser by that point. 95 return Promise.race([nativePromise, request.cancelPromise]); 96 }, 97 98 /** 99 * Requests an async flush for all non-lazy browsers of a given window. 100 * Returns a Promise that will resolve when we've heard back from all browsers. 101 */ 102 flushWindow(window) { 103 let promises = []; 104 for (let browser of window.gBrowser.browsers) { 105 if (window.gBrowser.getTabForBrowser(browser).linkedPanel) { 106 promises.push(this.flush(browser)); 107 } 108 } 109 return Promise.all(promises); 110 }, 111 112 /** 113 * Resolves all active flush requests for a given browser. This should be 114 * used when the content process crashed or the final update message was 115 * seen. In those cases we can't guarantee to ever hear back from the frame 116 * script so we just resolve all requests instead of discarding them. 117 * 118 * @param browser (<xul:browser>) 119 * The browser for which all flushes are being resolved. 120 * @param success (bool, optional) 121 * Whether or not the flushes succeeded. 122 * @param message (string, optional) 123 * An error message that will be sent to the Console in the 124 * event that the flushes failed. 125 */ 126 resolveAll(browser, success = true, message = "") { 127 // Nothing to do if there are no pending flushes for the given browser. 128 if (!this._requests.has(browser.permanentKey)) { 129 return; 130 } 131 132 // Retrieve the cancel function for a given browser. 133 let { cancel } = this._requests.get(browser.permanentKey); 134 135 if (!success) { 136 console.error("Failed to flush browser: ", message); 137 } 138 139 // Resolve all requests. 140 cancel(success); 141 }, 142 };