diff --git a/toolkit/components/extensions/ConduitsChild.jsm b/toolkit/components/extensions/ConduitsChild.jsm index 80db2d1253ab..5abb02daebfc 100644 --- a/toolkit/components/extensions/ConduitsChild.jsm +++ b/toolkit/components/extensions/ConduitsChild.jsm @@ -7,6 +7,12 @@ * This @file implements the child side of Conduits, an abstraction over * Fission IPC for extension API subject. See {@link ConduitsParent.jsm} * for more details about the overall design. + * + * @typedef {object} MessageData + * @prop {ConduitID} [target] + * @prop {ConduitID} [sender] + * @prop {boolean} query + * @prop {object} arg */ const EXPORTED_SYMBOLS = ["BaseConduit", "ConduitsChild"]; @@ -47,7 +53,7 @@ class BaseConduit { * @param {string} method * @param {boolean} query Flag indicating a response is expected. * @param {JSWindowActor} actor - * @param {{arg: object, sender: ConduitID}} data + * @param {MessageData} data * @returns {Promise?} */ _send(method, query, actor, data) { @@ -144,9 +150,18 @@ class ConduitsChild extends JSWindowActorChild { /** * JSWindowActor method, routes the message to the target subject. + * @param {string} name + * @param {MessageData|MessageData[]} data * @returns {Promise?} */ - receiveMessage({ name, data: { target, arg, query, sender } }) { + receiveMessage({ name, data }) { + // Batch of webRequest events, run each and return results, ignoring errors. + if (Array.isArray(data)) { + let run = data => this.receiveMessage({ name, data }); + return Promise.all(data.map(data => run(data).catch(Cu.reportError))); + } + + let { target, arg, query, sender } = data; let conduit = this.conduits.get(target); if (!conduit) { throw new Error(`${name} for closed conduit ${target}: ${uneval(arg)}`); diff --git a/toolkit/components/extensions/ConduitsParent.jsm b/toolkit/components/extensions/ConduitsParent.jsm index 274b0ee266d5..a841e2ffb5ab 100644 --- a/toolkit/components/extensions/ConduitsParent.jsm +++ b/toolkit/components/extensions/ConduitsParent.jsm @@ -52,6 +52,8 @@ const { BaseConduit } = ChromeUtils.import( "resource://gre/modules/ConduitsChild.jsm" ); +const BATCH_TIMEOUT_MS = 250; + /** * Internal, keeps track of all parent and remote (child) conduits. */ @@ -162,8 +164,13 @@ class BroadcastConduit extends BaseConduit { if (!this.open) { throw new Error(`send${method} on closed conduit ${this.id}`); } - let sender = this.address; + + let sender = this.id; let { actor } = Hub.remotes.get(target); + + if (method === "RunListener" && arg.path.startsWith("webRequest.")) { + return actor.batch(method, { target, arg, query, sender }); + } return super._send(method, query, actor, { target, arg, query, sender }); } @@ -188,10 +195,38 @@ class BroadcastConduit extends BaseConduit { class ConduitsParent extends JSWindowActorParent { constructor() { super(); + this.batchData = []; + this.batchPromise = null; + } + + /** + * Group webRequest events to send them as a batch, reducing IPC overhead. + * @param {string} name + * @param {MessageData} data + */ + async batch(name, data) { + let num = this.batchData.length; + this.batchData.push(data); + + if (!num) { + let resolve; + this.batchPromise = new Promise(r => (resolve = r)); + + let send = () => { + resolve(this.manager && this.sendQuery(name, this.batchData)); + this.batchData = []; + }; + ChromeUtils.idleDispatch(send, { timeout: BATCH_TIMEOUT_MS }); + } + + let results = await this.batchPromise; + return results && results[num]; } /** * JSWindowActor method, routes the message to the target subject. + * @param {string} name + * @param {MessageData} data * @returns {Promise?} */ receiveMessage({ name, data: { arg, query, sender } }) { diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm index b2723ed70dd1..9773cf0fad1b 100644 --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -1134,9 +1134,6 @@ ParentAPIManager = { let { childId } = data; let handlingUserInput = false; - // TODO: Bug 1587058 - Redesign webRequest event coelescing. - // let lowPriority = data.path.startsWith("webRequest."); - let listener = async (...listenerArgs) => { let result = await this.conduit.queryRunListener(childId, { childId,