From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- layout/tools/reftest/ReftestFissionChild.sys.mjs | 399 +++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 layout/tools/reftest/ReftestFissionChild.sys.mjs (limited to 'layout/tools/reftest/ReftestFissionChild.sys.mjs') diff --git a/layout/tools/reftest/ReftestFissionChild.sys.mjs b/layout/tools/reftest/ReftestFissionChild.sys.mjs new file mode 100644 index 0000000000..6eeece6639 --- /dev/null +++ b/layout/tools/reftest/ReftestFissionChild.sys.mjs @@ -0,0 +1,399 @@ +export class ReftestFissionChild extends JSWindowActorChild { + forwardAfterPaintEventToParent( + rects, + originalTargetUri, + dispatchToSelfAsWell + ) { + if (dispatchToSelfAsWell && this.contentWindow) { + let event = new this.contentWindow.CustomEvent( + "Reftest:MozAfterPaintFromChild", + { bubbles: true, detail: { rects, originalTargetUri } } + ); + this.contentWindow.dispatchEvent(event); + } + + let parentContext = this.browsingContext.parent; + if (parentContext) { + try { + this.sendAsyncMessage("ForwardAfterPaintEvent", { + toBrowsingContext: parentContext, + fromBrowsingContext: this.browsingContext, + rects, + originalTargetUri, + }); + } catch (e) { + // |this| can be destroyed here and unable to send messages, which is + // not a problem, the reftest harness probably torn down the page and + // moved on to the next test. + console.error(e); + } + } + } + + handleEvent(evt) { + switch (evt.type) { + case "MozAfterPaint": + // We want to forward any after paint events to our parent document so that + // that it reaches the root content document where the main reftest harness + // code (reftest-content.js) will process it and update the canvas. + var rects = []; + for (let r of evt.clientRects) { + rects.push({ + left: r.left, + top: r.top, + right: r.right, + bottom: r.bottom, + }); + } + this.forwardAfterPaintEventToParent( + rects, + this.document.documentURI, + /* dispatchToSelfAsWell */ false + ); + break; + } + } + + transformRect(transform, rect) { + let p1 = transform.transformPoint({ x: rect.left, y: rect.top }); + let p2 = transform.transformPoint({ x: rect.right, y: rect.top }); + let p3 = transform.transformPoint({ x: rect.left, y: rect.bottom }); + let p4 = transform.transformPoint({ x: rect.right, y: rect.bottom }); + let quad = new DOMQuad(p1, p2, p3, p4); + return quad.getBounds(); + } + + SetupDisplayportRoot() { + let returnStrings = { infoStrings: [], errorStrings: [] }; + + let contentRootElement = this.contentWindow.document.documentElement; + if (!contentRootElement) { + return Promise.resolve(returnStrings); + } + + // If we don't have the reftest-async-scroll attribute we only look at + // the root element for potential display ports to set. + if (!contentRootElement.hasAttribute("reftest-async-scroll")) { + let winUtils = this.contentWindow.windowUtils; + this.setupDisplayportForElement( + contentRootElement, + winUtils, + returnStrings + ); + return Promise.resolve(returnStrings); + } + + // Send a msg to the parent side to get the parent side to tell all + // process roots to do the displayport setting. + let browsingContext = this.browsingContext; + let promise = this.sendQuery("TellChildrenToSetupDisplayport", { + browsingContext, + }); + return promise.then( + function (result) { + for (let errorString of result.errorStrings) { + returnStrings.errorStrings.push(errorString); + } + for (let infoString of result.infoStrings) { + returnStrings.infoStrings.push(infoString); + } + return returnStrings; + }, + function (reason) { + returnStrings.errorStrings.push( + "SetupDisplayport SendQuery to parent promise rejected: " + reason + ); + return returnStrings; + } + ); + } + + attrOrDefault(element, attr, def) { + return element.hasAttribute(attr) + ? Number(element.getAttribute(attr)) + : def; + } + + setupDisplayportForElement(element, winUtils, returnStrings) { + var dpw = this.attrOrDefault(element, "reftest-displayport-w", 0); + var dph = this.attrOrDefault(element, "reftest-displayport-h", 0); + var dpx = this.attrOrDefault(element, "reftest-displayport-x", 0); + var dpy = this.attrOrDefault(element, "reftest-displayport-y", 0); + if (dpw !== 0 || dph !== 0 || dpx != 0 || dpy != 0) { + returnStrings.infoStrings.push( + "Setting displayport to " + ); + winUtils.setDisplayPortForElement(dpx, dpy, dpw, dph, element, 1); + } + } + + setupDisplayportForElementSubtree(element, winUtils, returnStrings) { + this.setupDisplayportForElement(element, winUtils, returnStrings); + for (let c = element.firstElementChild; c; c = c.nextElementSibling) { + this.setupDisplayportForElementSubtree(c, winUtils, returnStrings); + } + if ( + typeof element.contentDocument !== "undefined" && + element.contentDocument + ) { + returnStrings.infoStrings.push( + "setupDisplayportForElementSubtree descending into subdocument" + ); + this.setupDisplayportForElementSubtree( + element.contentDocument.documentElement, + element.contentWindow.windowUtils, + returnStrings + ); + } + } + + setupAsyncScrollOffsetsForElement( + element, + winUtils, + allowFailure, + returnStrings + ) { + let sx = this.attrOrDefault(element, "reftest-async-scroll-x", 0); + let sy = this.attrOrDefault(element, "reftest-async-scroll-y", 0); + if (sx != 0 || sy != 0) { + try { + // This might fail when called from RecordResult since layers + // may not have been constructed yet + winUtils.setAsyncScrollOffset(element, sx, sy); + return true; + } catch (e) { + if (allowFailure) { + returnStrings.infoStrings.push( + "setupAsyncScrollOffsetsForElement error calling setAsyncScrollOffset: " + + e + ); + } else { + returnStrings.errorStrings.push( + "setupAsyncScrollOffsetsForElement error calling setAsyncScrollOffset: " + + e + ); + } + } + } + return false; + } + + setupAsyncScrollOffsetsForElementSubtree( + element, + winUtils, + allowFailure, + returnStrings + ) { + let updatedAny = this.setupAsyncScrollOffsetsForElement( + element, + winUtils, + returnStrings + ); + for (let c = element.firstElementChild; c; c = c.nextElementSibling) { + if ( + this.setupAsyncScrollOffsetsForElementSubtree( + c, + winUtils, + allowFailure, + returnStrings + ) + ) { + updatedAny = true; + } + } + if ( + typeof element.contentDocument !== "undefined" && + element.contentDocument + ) { + returnStrings.infoStrings.push( + "setupAsyncScrollOffsetsForElementSubtree Descending into subdocument" + ); + if ( + this.setupAsyncScrollOffsetsForElementSubtree( + element.contentDocument.documentElement, + element.contentWindow.windowUtils, + allowFailure, + returnStrings + ) + ) { + updatedAny = true; + } + } + return updatedAny; + } + + async receiveMessage(msg) { + switch (msg.name) { + case "ForwardAfterPaintEventToSelfAndParent": { + // The embedderElement can be null if the child we got this from was removed. + // Not much we can do to transform the rects, but it doesn't matter, the rects + // won't reach reftest-content.js. + if (msg.data.fromBrowsingContext.embedderElement == null) { + this.forwardAfterPaintEventToParent( + msg.data.rects, + msg.data.originalTargetUri, + /* dispatchToSelfAsWell */ true + ); + return undefined; + } + + let translate = new DOMMatrixReadOnly().translate(0, 0); + if (this.contentWindow) { + // Transform the rects from fromBrowsingContext to us. + // We first translate from the content rect to the border rect of the iframe. + let style = this.contentWindow.getComputedStyle( + msg.data.fromBrowsingContext.embedderElement + ); + translate = new DOMMatrixReadOnly().translate( + parseFloat(style.paddingLeft) + parseFloat(style.borderLeftWidth), + parseFloat(style.paddingTop) + parseFloat(style.borderTopWidth) + ); + } + + // Then we transform from the iframe to our root frame. + // We are guaranteed to be the process with the embedderElement for fromBrowsingContext. + let transform = + msg.data.fromBrowsingContext.embedderElement.getTransformToViewport(); + let combined = translate.multiply(transform); + + let newrects = msg.data.rects.map(r => this.transformRect(combined, r)); + + this.forwardAfterPaintEventToParent( + newrects, + msg.data.originalTargetUri, + /* dispatchToSelfAsWell */ true + ); + break; + } + + case "EmptyMessage": + return undefined; + case "UpdateLayerTree": { + let errorStrings = []; + try { + if (this.manager.isProcessRoot) { + this.contentWindow.windowUtils.updateLayerTree(); + } + } catch (e) { + errorStrings.push("updateLayerTree failed: " + e); + } + return { errorStrings }; + } + case "FlushRendering": { + let errorStrings = []; + let warningStrings = []; + let infoStrings = []; + + try { + let { ignoreThrottledAnimations, needsAnimationFrame } = msg.data; + + if (this.manager.isProcessRoot) { + var anyPendingPaintsGeneratedInDescendants = false; + + if (needsAnimationFrame) { + await new Promise(resolve => + this.contentWindow.requestAnimationFrame(resolve) + ); + } + + function flushWindow(win) { + var utils = win.windowUtils; + var afterPaintWasPending = utils.isMozAfterPaintPending; + + var root = win.document.documentElement; + if (root && !root.classList.contains("reftest-no-flush")) { + try { + if (ignoreThrottledAnimations) { + utils.flushLayoutWithoutThrottledAnimations(); + } else { + root.getBoundingClientRect(); + } + } catch (e) { + warningStrings.push("flushWindow failed: " + e + "\n"); + } + } + + if (!afterPaintWasPending && utils.isMozAfterPaintPending) { + infoStrings.push( + "FlushRendering generated paint for window " + + win.location.href + ); + anyPendingPaintsGeneratedInDescendants = true; + } + + for (let i = 0; i < win.frames.length; ++i) { + try { + if (!Cu.isRemoteProxy(win.frames[i])) { + flushWindow(win.frames[i]); + } + } catch (e) { + console.error(e); + } + } + } + + // `contentWindow` will be null if the inner window for this actor + // has been navigated away from. + if (this.contentWindow) { + flushWindow(this.contentWindow); + } + + if ( + anyPendingPaintsGeneratedInDescendants && + !this.contentWindow.windowUtils.isMozAfterPaintPending + ) { + warningStrings.push( + "Internal error: descendant frame generated a MozAfterPaint event, but the root document doesn't have one!" + ); + } + } + } catch (e) { + errorStrings.push("flushWindow failed: " + e); + } + return { errorStrings, warningStrings, infoStrings }; + } + + case "SetupDisplayport": { + let contentRootElement = this.document.documentElement; + let winUtils = this.contentWindow.windowUtils; + let returnStrings = { infoStrings: [], errorStrings: [] }; + if (contentRootElement) { + this.setupDisplayportForElementSubtree( + contentRootElement, + winUtils, + returnStrings + ); + } + return returnStrings; + } + + case "SetupAsyncScrollOffsets": { + let returns = { infoStrings: [], errorStrings: [], updatedAny: false }; + let contentRootElement = this.document.documentElement; + + if (!contentRootElement) { + return returns; + } + + let winUtils = this.contentWindow.windowUtils; + + returns.updatedAny = this.setupAsyncScrollOffsetsForElementSubtree( + contentRootElement, + winUtils, + msg.data.allowFailure, + returns + ); + return returns; + } + } + return undefined; + } +} -- cgit v1.2.3