export class ReftestFissionParent extends JSWindowActorParent { tellChildrenToFlushRendering( browsingContext, ignoreThrottledAnimations, needsAnimationFrame ) { let promises = []; this.tellChildrenToFlushRenderingRecursive( browsingContext, ignoreThrottledAnimations, needsAnimationFrame, promises ); return Promise.allSettled(promises); } tellChildrenToFlushRenderingRecursive( browsingContext, ignoreThrottledAnimations, needsAnimationFrame, promises ) { let cwg = browsingContext.currentWindowGlobal; if (cwg && cwg.isProcessRoot) { let a = cwg.getActor("ReftestFission"); if (a) { let responsePromise = a.sendQuery("FlushRendering", { ignoreThrottledAnimations, needsAnimationFrame, }); promises.push(responsePromise); } } for (let context of browsingContext.children) { this.tellChildrenToFlushRenderingRecursive( context, ignoreThrottledAnimations, needsAnimationFrame, promises ); } } // not including browsingContext getNearestProcessRootProperDescendants(browsingContext) { let result = []; for (let context of browsingContext.children) { this.getNearestProcessRootProperDescendantsRecursive(context, result); } return result; } getNearestProcessRootProperDescendantsRecursive(browsingContext, result) { let cwg = browsingContext.currentWindowGlobal; if (cwg && cwg.isProcessRoot) { result.push(browsingContext); return; } for (let context of browsingContext.children) { this.getNearestProcessRootProperDescendantsRecursive(context, result); } } // tell children and itself async tellChildrenToUpdateLayerTree(browsingContext) { let errorStrings = []; let infoStrings = []; let cwg = browsingContext.currentWindowGlobal; if (!cwg || !cwg.isProcessRoot) { if (cwg) { errorStrings.push( "tellChildrenToUpdateLayerTree called on a non process root?" ); } return { errorStrings, infoStrings }; } let actor = cwg.getActor("ReftestFission"); if (!actor) { return { errorStrings, infoStrings }; } // When we paint a document we also update the EffectsInfo visible rect in // nsSubDocumentFrame for any remote subdocuments. This visible rect is // used to limit painting for the subdocument in the subdocument's process. // So we want to ensure that the IPC message that updates the visible rect // to the subdocument's process arrives before we paint the subdocument // (otherwise our painting might not be up to date). We do this by sending, // and waiting for reply, an "EmptyMessage" to every direct descendant that // is in another process. Since we send the "EmptyMessage" after the // visible rect update message we know that the visible rect will be // updated by the time we hear back from the "EmptyMessage". Then we can // ask the subdocument process to paint. try { let result = await actor.sendQuery("UpdateLayerTree"); errorStrings.push(...result.errorStrings); } catch (e) { infoStrings.push( "tellChildrenToUpdateLayerTree UpdateLayerTree msg to child rejected: " + e ); } let descendants = actor.getNearestProcessRootProperDescendants(browsingContext); for (let context of descendants) { let cwg2 = context.currentWindowGlobal; if (cwg2) { if (!cwg2.isProcessRoot) { errorStrings.push( "getNearestProcessRootProperDescendants returned a non process root?" ); } let actor2 = cwg2.getActor("ReftestFission"); if (actor2) { try { await actor2.sendQuery("EmptyMessage"); } catch (e) { infoStrings.push( "tellChildrenToUpdateLayerTree EmptyMessage msg to child rejected: " + e ); } try { let result2 = await actor2.tellChildrenToUpdateLayerTree(context); errorStrings.push(...result2.errorStrings); infoStrings.push(...result2.infoStrings); } catch (e) { errorStrings.push( "tellChildrenToUpdateLayerTree recursive tellChildrenToUpdateLayerTree call rejected: " + e ); } } } } return { errorStrings, infoStrings }; } tellChildrenToSetupDisplayport(browsingContext, promises) { let cwg = browsingContext.currentWindowGlobal; if (cwg && cwg.isProcessRoot) { let a = cwg.getActor("ReftestFission"); if (a) { let responsePromise = a.sendQuery("SetupDisplayport"); promises.push(responsePromise); } } for (let context of browsingContext.children) { this.tellChildrenToSetupDisplayport(context, promises); } } tellChildrenToSetupAsyncScrollOffsets( browsingContext, allowFailure, promises ) { let cwg = browsingContext.currentWindowGlobal; if (cwg && cwg.isProcessRoot) { let a = cwg.getActor("ReftestFission"); if (a) { let responsePromise = a.sendQuery("SetupAsyncScrollOffsets", { allowFailure, }); promises.push(responsePromise); } } for (let context of browsingContext.children) { this.tellChildrenToSetupAsyncScrollOffsets( context, allowFailure, promises ); } } receiveMessage(msg) { switch (msg.name) { case "ForwardAfterPaintEvent": { let cwg = msg.data.toBrowsingContext.currentWindowGlobal; if (cwg) { let a = cwg.getActor("ReftestFission"); if (a) { a.sendAsyncMessage( "ForwardAfterPaintEventToSelfAndParent", msg.data ); } } break; } case "FlushRendering": { let promise = this.tellChildrenToFlushRendering( msg.data.browsingContext, msg.data.ignoreThrottledAnimations, msg.data.needsAnimationFrame ); return promise.then(function (results) { let errorStrings = []; let warningStrings = []; let infoStrings = []; for (let r of results) { if (r.status != "fulfilled") { if (r.status == "pending") { errorStrings.push( "FlushRendering sendQuery to child promise still pending?" ); } else { // We expect actors to go away causing sendQuery's to fail, so // just note it. infoStrings.push( "FlushRendering sendQuery to child promise rejected: " + r.reason ); } continue; } errorStrings.push(...r.value.errorStrings); warningStrings.push(...r.value.warningStrings); infoStrings.push(...r.value.infoStrings); } return { errorStrings, warningStrings, infoStrings }; }); } case "UpdateLayerTree": { return this.tellChildrenToUpdateLayerTree(msg.data.browsingContext); } case "TellChildrenToSetupDisplayport": { let promises = []; this.tellChildrenToSetupDisplayport(msg.data.browsingContext, promises); return Promise.allSettled(promises).then(function (results) { let errorStrings = []; let infoStrings = []; for (let r of results) { if (r.status != "fulfilled") { // We expect actors to go away causing sendQuery's to fail, so // just note it. infoStrings.push( "SetupDisplayport sendQuery to child promise rejected: " + r.reason ); continue; } errorStrings.push(...r.value.errorStrings); infoStrings.push(...r.value.infoStrings); } return { errorStrings, infoStrings }; }); } case "SetupAsyncScrollOffsets": { let promises = []; this.tellChildrenToSetupAsyncScrollOffsets( this.manager.browsingContext, msg.data.allowFailure, promises ); return Promise.allSettled(promises).then(function (results) { let errorStrings = []; let infoStrings = []; let updatedAny = false; for (let r of results) { if (r.status != "fulfilled") { // We expect actors to go away causing sendQuery's to fail, so // just note it. infoStrings.push( "SetupAsyncScrollOffsets sendQuery to child promise rejected: " + r.reason ); continue; } errorStrings.push(...r.value.errorStrings); infoStrings.push(...r.value.infoStrings); if (r.value.updatedAny) { updatedAny = true; } } return { errorStrings, infoStrings, updatedAny }; }); } } return undefined; } }