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
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 loader
.lazyRequireGetter(
10 "resource://devtools/shared/DevToolsUtils.js"
13 ChromeUtils
.defineESModuleGetters(
16 NetUtil
: "resource://gre/modules/NetUtil.sys.mjs",
18 { global
: "contextual" }
24 author
: "AUTHOR_SHEET",
27 // eslint-disable-next-line no-unused-vars
28 loader
.lazyRequireGetter(
30 "setIgnoreLayoutChanges",
31 "resource://devtools/server/actors/reflow.js",
34 exports
.setIgnoreLayoutChanges
= (...args
) =>
35 this.setIgnoreLayoutChanges(...args
);
38 * Check a window is part of the boundary window given.
40 * @param {DOMWindow} boundaryWindow
41 * @param {DOMWindow} win
44 function isWindowIncluded(boundaryWindow
, win
) {
45 if (win
=== boundaryWindow
) {
49 const parent
= win
.parent
;
51 if (!parent
|| parent
=== win
) {
55 return isWindowIncluded(boundaryWindow
, parent
);
57 exports
.isWindowIncluded
= isWindowIncluded
;
60 * like win.frameElement, but goes through mozbrowsers and mozapps iframes.
62 * @param {DOMWindow} win
63 * The window to get the frame for
65 * The element in which the window is embedded.
67 const getFrameElement
= win
=> {
68 const isTopWindow
= win
&& DevToolsUtils
.getTopWindow(win
) === win
;
69 return isTopWindow
? null : win
.browsingContext
.embedderElement
;
71 exports
.getFrameElement
= getFrameElement
;
74 * Get the x/y offsets for of all the parent frames of a given node, limited to
75 * the boundary window given.
77 * @param {DOMWindow} boundaryWindow
78 * The window where to stop to iterate. If `null` is given, the top
80 * @param {DOMNode} node
81 * The node for which we are to get the offset
83 * The frame offset [x, y]
85 function getFrameOffsets(boundaryWindow
, node
) {
89 let frameWin
= getWindowFor(node
);
90 const scale
= getCurrentZoom(node
);
92 if (boundaryWindow
=== null) {
93 boundaryWindow
= DevToolsUtils
.getTopWindow(frameWin
);
94 } else if (typeof boundaryWindow
=== "undefined") {
95 throw new Error("No boundaryWindow given. Use null for the default one.");
98 while (frameWin
!== boundaryWindow
) {
99 const frameElement
= getFrameElement(frameWin
);
104 // We are in an iframe.
105 // We take into account the parent iframe position and its
106 // offset (borders and padding).
107 const frameRect
= frameElement
.getBoundingClientRect();
109 const [offsetTop
, offsetLeft
] = getFrameContentOffset(frameElement
);
111 xOffset
+= frameRect
.left
+ offsetLeft
;
112 yOffset
+= frameRect
.top
+ offsetTop
;
114 frameWin
= frameWin
.parent
;
117 return [xOffset
* scale
, yOffset
* scale
];
119 exports
.getFrameOffsets
= getFrameOffsets
;
122 * Get box quads adjusted for iframes and zoom level.
124 * Warning: this function returns things that look like DOMQuad objects but
125 * aren't (they resemble an old version of the spec). Unlike the return value
126 * of node.getBoxQuads, they have a .bounds property and not a .getBounds()
129 * @param {DOMWindow} boundaryWindow
130 * The window where to stop to iterate. If `null` is given, the top
132 * @param {DOMNode} node
133 * The node for which we are to get the box model region
135 * @param {String} region
136 * The box model region to return: "content", "padding", "border" or
138 * @param {Object} [options.ignoreZoom=false]
139 * Ignore zoom used in the context of e.g. canvas.
141 * An array of objects that have the same structure as quads returned by
142 * getBoxQuads. An empty array if the node has no quads or is invalid.
144 function getAdjustedQuads(
148 { ignoreZoom
, ignoreScroll
} = {}
150 if (!node
|| !node
.getBoxQuads
) {
154 const quads
= node
.getBoxQuads({
156 relativeTo
: boundaryWindow
.document
,
157 createFramesForSuppressedWhitespace
: false,
164 const scale
= ignoreZoom
? 1 : getCurrentZoom(node
);
165 const { scrollX
, scrollY
} = ignoreScroll
166 ? { scrollX
: 0, scrollY
: 0 }
169 const xOffset
= scrollX
* scale
;
170 const yOffset
= scrollY
* scale
;
172 const adjustedQuads
= [];
173 for (const quad
of quads
) {
174 const bounds
= quad
.getBounds();
177 w
: quad
.p1
.w
* scale
,
178 x
: quad
.p1
.x
* scale
+ xOffset
,
179 y
: quad
.p1
.y
* scale
+ yOffset
,
180 z
: quad
.p1
.z
* scale
,
183 w
: quad
.p2
.w
* scale
,
184 x
: quad
.p2
.x
* scale
+ xOffset
,
185 y
: quad
.p2
.y
* scale
+ yOffset
,
186 z
: quad
.p2
.z
* scale
,
189 w
: quad
.p3
.w
* scale
,
190 x
: quad
.p3
.x
* scale
+ xOffset
,
191 y
: quad
.p3
.y
* scale
+ yOffset
,
192 z
: quad
.p3
.z
* scale
,
195 w
: quad
.p4
.w
* scale
,
196 x
: quad
.p4
.x
* scale
+ xOffset
,
197 y
: quad
.p4
.y
* scale
+ yOffset
,
198 z
: quad
.p4
.z
* scale
,
201 bottom
: bounds
.bottom
* scale
+ yOffset
,
202 height
: bounds
.height
* scale
,
203 left
: bounds
.left
* scale
+ xOffset
,
204 right
: bounds
.right
* scale
+ xOffset
,
205 top
: bounds
.top
* scale
+ yOffset
,
206 width
: bounds
.width
* scale
,
207 x
: bounds
.x
* scale
+ xOffset
,
208 y
: bounds
.y
* scale
+ yOffset
,
213 return adjustedQuads
;
215 exports
.getAdjustedQuads
= getAdjustedQuads
;
218 * Compute the absolute position and the dimensions of a node, relativalely
219 * to the root window.
221 * @param {DOMWindow} boundaryWindow
222 * The window where to stop to iterate. If `null` is given, the top
224 * @param {DOMNode} node
225 * a DOM element to get the bounds for
226 * @param {DOMWindow} contentWindow
227 * the content window holding the node
229 * A rect object with the {top, left, width, height} properties
231 function getRect(boundaryWindow
, node
, contentWindow
) {
232 let frameWin
= node
.ownerDocument
.defaultView
;
233 const clientRect
= node
.getBoundingClientRect();
235 if (boundaryWindow
=== null) {
236 boundaryWindow
= DevToolsUtils
.getTopWindow(frameWin
);
237 } else if (typeof boundaryWindow
=== "undefined") {
238 throw new Error("No boundaryWindow given. Use null for the default one.");
241 // Go up in the tree of frames to determine the correct rectangle.
242 // clientRect is read-only, we need to be able to change properties.
244 top
: clientRect
.top
+ contentWindow
.pageYOffset
,
245 left
: clientRect
.left
+ contentWindow
.pageXOffset
,
246 width
: clientRect
.width
,
247 height
: clientRect
.height
,
250 // We iterate through all the parent windows.
251 while (frameWin
!== boundaryWindow
) {
252 const frameElement
= getFrameElement(frameWin
);
257 // We are in an iframe.
258 // We take into account the parent iframe position and its
259 // offset (borders and padding).
260 const frameRect
= frameElement
.getBoundingClientRect();
262 const [offsetTop
, offsetLeft
] = getFrameContentOffset(frameElement
);
264 rect
.top
+= frameRect
.top
+ offsetTop
;
265 rect
.left
+= frameRect
.left
+ offsetLeft
;
267 frameWin
= frameWin
.parent
;
272 exports
.getRect
= getRect
;
275 * Get the 4 bounding points for a node taking iframes into account.
276 * Note that for transformed nodes, this will return the untransformed bound.
278 * @param {DOMWindow} boundaryWindow
279 * The window where to stop to iterate. If `null` is given, the top
281 * @param {DOMNode} node
283 * An object with p1,p2,p3,p4 properties being {x,y} objects
285 function getNodeBounds(boundaryWindow
, node
) {
289 const { scrollX
, scrollY
} = boundaryWindow
;
290 const scale
= getCurrentZoom(node
);
292 // Find out the offset of the node in its current frame
296 while (el
?.parentNode
) {
297 offsetLeft
+= el
.offsetLeft
;
298 offsetTop
+= el
.offsetTop
;
299 el
= el
.offsetParent
;
302 // Also take scrolled containers into account
304 while (el
?.parentNode
) {
306 offsetTop
-= el
.scrollTop
;
309 offsetLeft
-= el
.scrollLeft
;
314 // And add the potential frame offset if the node is nested
315 let [xOffset
, yOffset
] = getFrameOffsets(boundaryWindow
, node
);
316 xOffset
+= (offsetLeft
+ scrollX
) * scale
;
317 yOffset
+= (offsetTop
+ scrollY
) * scale
;
319 // Get the width and height
320 const width
= node
.offsetWidth
* scale
;
321 const height
= node
.offsetHeight
* scale
;
324 p1
: { x
: xOffset
, y
: yOffset
},
325 p2
: { x
: xOffset
+ width
, y
: yOffset
},
326 p3
: { x
: xOffset
+ width
, y
: yOffset
+ height
},
327 p4
: { x
: xOffset
, y
: yOffset
+ height
},
329 right
: xOffset
+ width
,
330 bottom
: yOffset
+ height
,
336 exports
.getNodeBounds
= getNodeBounds
;
339 * Same as doing iframe.contentWindow but works with all types of container
340 * elements that act like frames (e.g. <embed>), where 'contentWindow' isn't a
341 * property that can be accessed.
342 * This uses the inIDeepTreeWalker instead.
343 * @param {DOMNode} frame
346 function safelyGetContentWindow(frame
) {
347 if (frame
.contentWindow
) {
348 return frame
.contentWindow
;
351 const walker
= Cc
["@mozilla.org/inspector/deep-tree-walker;1"].createInstance(
354 walker
.showSubDocuments
= true;
355 walker
.showDocumentsAsNodes
= true;
357 walker
.currentNode
= frame
;
359 const document
= walker
.nextNode();
360 if (!document
|| !document
.defaultView
) {
361 throw new Error("Couldn't get the content window inside frame " + frame
);
364 return document
.defaultView
;
368 * Returns a frame's content offset (frame border + padding).
369 * Note: this function shouldn't need to exist, had the platform provided a
370 * suitable API for determining the offset between the frame's content and
371 * its bounding client rect. Bug 626359 should provide us with such an API.
373 * @param {DOMNode} frame
375 * @return {Array} [offsetTop, offsetLeft]
376 * offsetTop is the distance from the top of the frame and the top of
377 * the content document.
378 * offsetLeft is the distance from the left of the frame and the left
379 * of the content document.
381 function getFrameContentOffset(frame
) {
382 const style
= safelyGetContentWindow(frame
).getComputedStyle(frame
);
384 // In some cases, the computed style is null
389 const paddingTop
= parseInt(style
.getPropertyValue("padding-top"), 10);
390 const paddingLeft
= parseInt(style
.getPropertyValue("padding-left"), 10);
392 const borderTop
= parseInt(style
.getPropertyValue("border-top-width"), 10);
393 const borderLeft
= parseInt(style
.getPropertyValue("border-left-width"), 10);
395 return [borderTop
+ paddingTop
, borderLeft
+ paddingLeft
];
399 * Check if a node and its document are still alive
400 * and attached to the window.
402 * @param {DOMNode} node
405 function isNodeConnected(node
) {
406 if (!node
.ownerDocument
|| !node
.ownerDocument
.defaultView
) {
412 node
.compareDocumentPosition(node
.ownerDocument
.documentElement
) &
413 node
.DOCUMENT_POSITION_DISCONNECTED
416 // "can't access dead object" error
420 exports
.isNodeConnected
= isNodeConnected
;
423 * Determine whether a node is anonymous.
425 * @param {DOMNode} node
428 * FIXME(bug 1597411): Remove one of these (or both, as
429 * `node.isNativeAnonymous` is quite clear).
431 const isAnonymous
= node
=> node
.isNativeAnonymous
;
432 exports
.isAnonymous
= isAnonymous
;
433 exports
.isNativeAnonymous
= isAnonymous
;
436 * Determine whether a node is a template element.
438 * @param {DOMNode} node
441 function isTemplateElement(node
) {
443 node
.ownerGlobal
&& node
.ownerGlobal
.HTMLTemplateElement
.isInstance(node
)
446 exports
.isTemplateElement
= isTemplateElement
;
449 * Determine whether a node is a shadow root.
451 * @param {DOMNode} node
454 const isShadowRoot
= node
=> node
.containingShadowRoot
== node
;
455 exports
.isShadowRoot
= isShadowRoot
;
458 * Gets the shadow root mode (open or closed).
460 * @param {DOMNode} node
461 * @return {String|null}
463 function getShadowRootMode(node
) {
464 return isShadowRoot(node
) ? node
.mode
: null;
466 exports
.getShadowRootMode
= getShadowRootMode
;
469 * Determine whether a node is a shadow host, ie. an element that has a shadowRoot
470 * attached to itself.
472 * @param {DOMNode} node
475 function isShadowHost(node
) {
476 const shadowRoot
= node
.openOrClosedShadowRoot
;
477 return shadowRoot
&& shadowRoot
.nodeType
=== Node
.DOCUMENT_FRAGMENT_NODE
;
479 exports
.isShadowHost
= isShadowHost
;
482 * Determine whether a node is a child of a shadow host. Even if the element has been
483 * assigned to a slot in the attached shadow DOM, the parent node for this element is
484 * still considered to be the "host" element, and we need to walk them differently.
486 * @param {DOMNode} node
489 function isDirectShadowHostChild(node
) {
490 // Pseudo elements and native anonymous elements are always part of the anonymous tree.
492 isMarkerPseudoElement(node
) ||
493 isBeforePseudoElement(node
) ||
494 isAfterPseudoElement(node
) ||
495 node
.isNativeAnonymous
500 const parentNode
= node
.parentNode
;
501 return parentNode
&& !!parentNode
.openOrClosedShadowRoot
;
503 exports
.isDirectShadowHostChild
= isDirectShadowHostChild
;
506 * Determine whether a node is a ::marker pseudo.
508 * @param {DOMNode} node
511 function isMarkerPseudoElement(node
) {
512 return node
.nodeName
=== "_moz_generated_content_marker";
514 exports
.isMarkerPseudoElement
= isMarkerPseudoElement
;
517 * Determine whether a node is a ::before pseudo.
519 * @param {DOMNode} node
522 function isBeforePseudoElement(node
) {
523 return node
.nodeName
=== "_moz_generated_content_before";
525 exports
.isBeforePseudoElement
= isBeforePseudoElement
;
528 * Determine whether a node is a ::after pseudo.
530 * @param {DOMNode} node
533 function isAfterPseudoElement(node
) {
534 return node
.nodeName
=== "_moz_generated_content_after";
536 exports
.isAfterPseudoElement
= isAfterPseudoElement
;
539 * Get the current zoom factor applied to the container window of a given node.
540 * @param {DOMNode|DOMWindow}
541 * The node for which the zoom factor should be calculated, or its
545 function getCurrentZoom(node
) {
546 const win
= getWindowFor(node
);
549 throw new Error("Unable to get the zoom from the given argument.");
552 return win
.browsingContext
?.fullZoom
|| 1.0;
554 exports
.getCurrentZoom
= getCurrentZoom
;
557 * Get the display pixel ratio for a given window.
558 * The `devicePixelRatio` property is affected by the zoom (see bug 809788), so we have to
559 * divide by the zoom value in order to get just the display density, expressed as pixel
560 * ratio (the physical display pixel compares to a pixel on a “normal” density screen).
562 * @param {DOMNode|DOMWindow}
563 * The node for which the zoom factor should be calculated, or its
567 function getDisplayPixelRatio(node
) {
568 const win
= getWindowFor(node
);
569 return win
.devicePixelRatio
/ getCurrentZoom(node
);
571 exports
.getDisplayPixelRatio
= getDisplayPixelRatio
;
574 * Returns the window's dimensions for the `window` given.
576 * @return {Object} An object with `width` and `height` properties, representing the
577 * number of pixels for the document's size.
579 function getWindowDimensions(window
) {
580 // First we'll try without flushing layout, because it's way faster.
581 const { windowUtils
} = window
;
582 let { width
, height
} = windowUtils
.getRootBounds();
584 if (!width
|| !height
) {
585 // We need a flush after all :'(
586 width
= window
.innerWidth
+ window
.scrollMaxX
- window
.scrollMinX
;
587 height
= window
.innerHeight
+ window
.scrollMaxY
- window
.scrollMinY
;
589 const scrollbarHeight
= {};
590 const scrollbarWidth
= {};
591 windowUtils
.getScrollbarSize(false, scrollbarWidth
, scrollbarHeight
);
592 width
-= scrollbarWidth
.value
;
593 height
-= scrollbarHeight
.value
;
596 return { width
, height
};
598 exports
.getWindowDimensions
= getWindowDimensions
;
601 * Returns the viewport's dimensions for the `window` given.
603 * @return {Object} An object with `width` and `height` properties, representing the
604 * number of pixels for the viewport's size.
606 function getViewportDimensions(window
) {
607 const { windowUtils
} = window
;
609 const scrollbarHeight
= {};
610 const scrollbarWidth
= {};
611 windowUtils
.getScrollbarSize(false, scrollbarWidth
, scrollbarHeight
);
613 const width
= window
.innerWidth
- scrollbarWidth
.value
;
614 const height
= window
.innerHeight
- scrollbarHeight
.value
;
616 return { width
, height
};
618 exports
.getViewportDimensions
= getViewportDimensions
;
621 * Return the default view for a given node, where node can be:
623 * - the document node
624 * - the window itself
625 * @param {DOMNode|DOMWindow|DOMDocument} node The node to get the window for.
626 * @return {DOMWindow}
628 function getWindowFor(node
) {
629 if (Node
.isInstance(node
)) {
630 if (node
.nodeType
=== node
.DOCUMENT_NODE
) {
631 return node
.defaultView
;
633 return node
.ownerDocument
.defaultView
;
634 } else if (node
instanceof Ci
.nsIDOMWindow
) {
641 * Synchronously loads a style sheet from `uri` and adds it to the list of
642 * additional style sheets of the document.
643 * The sheets added takes effect immediately, and only on the document of the
646 * @param {DOMWindow} window
647 * @param {String} url
648 * @param {String} [type="agent"]
650 function loadSheet(window
, url
, type
= "agent") {
651 if (!(type
in SHEET_TYPE
)) {
655 const { windowUtils
} = window
;
657 windowUtils
.loadSheetUsingURIString(url
, windowUtils
[SHEET_TYPE
[type
]]);
659 // The method fails if the url is already loaded.
662 exports
.loadSheet
= loadSheet
;
665 * Remove the document style sheet at `sheetURI` from the list of additional
666 * style sheets of the document. The removal takes effect immediately.
668 * @param {DOMWindow} window
669 * @param {String} url
670 * @param {String} [type="agent"]
672 function removeSheet(window
, url
, type
= "agent") {
673 if (!(type
in SHEET_TYPE
)) {
677 const { windowUtils
} = window
;
679 windowUtils
.removeSheetUsingURIString(url
, windowUtils
[SHEET_TYPE
[type
]]);
681 // The method fails if the url is already removed.
684 exports
.removeSheet
= removeSheet
;
687 * Get the untransformed coordinates for a node.
689 * @param {DOMNode} node
690 * The node for which the DOMQuad is to be returned.
691 * @param {String} region
692 * The box model region to return: "content", "padding", "border" or
695 * A DOMQuad representation of the node.
697 function getUntransformedQuad(node
, region
= "border") {
698 // Get the inverse transformation matrix for the node.
699 const matrix
= node
.getTransformToViewport();
700 const inverse
= matrix
.inverse();
701 const win
= node
.ownerGlobal
;
703 // Get the adjusted quads for the node (including scroll offsets).
704 const quads
= getAdjustedQuads(win
, node
, region
, {
708 // Create DOMPoints from the transformed node position.
709 const p1
= new DOMPoint(quads
[0].p1
.x
, quads
[0].p1
.y
);
710 const p2
= new DOMPoint(quads
[0].p2
.x
, quads
[0].p2
.y
);
711 const p3
= new DOMPoint(quads
[0].p3
.x
, quads
[0].p3
.y
);
712 const p4
= new DOMPoint(quads
[0].p4
.x
, quads
[0].p4
.y
);
714 // Apply the inverse transformation matrix to the points to get the
715 // untransformed points.
716 const ip1
= inverse
.transformPoint(p1
);
717 const ip2
= inverse
.transformPoint(p2
);
718 const ip3
= inverse
.transformPoint(p3
);
719 const ip4
= inverse
.transformPoint(p4
);
721 // Save the results in a DOMQuad.
722 const quad
= new DOMQuad(
723 { x
: ip1
.x
, y
: ip1
.y
},
724 { x
: ip2
.x
, y
: ip2
.y
},
725 { x
: ip3
.x
, y
: ip3
.y
},
726 { x
: ip4
.x
, y
: ip4
.y
}
729 // Remove the border offsets because we include them when calculating
730 // offsets in the while loop.
731 const style
= win
.getComputedStyle(node
);
732 const leftAdjustment
= parseInt(style
.borderLeftWidth
, 10) || 0;
733 const topAdjustment
= parseInt(style
.borderTopWidth
, 10) || 0;
735 quad
.p1
.x
-= leftAdjustment
;
736 quad
.p2
.x
-= leftAdjustment
;
737 quad
.p3
.x
-= leftAdjustment
;
738 quad
.p4
.x
-= leftAdjustment
;
739 quad
.p1
.y
-= topAdjustment
;
740 quad
.p2
.y
-= topAdjustment
;
741 quad
.p3
.y
-= topAdjustment
;
742 quad
.p4
.y
-= topAdjustment
;
744 // Calculate offsets.
746 const nodeStyle
= win
.getComputedStyle(node
);
747 const borderLeftWidth
= parseInt(nodeStyle
.borderLeftWidth
, 10) || 0;
748 const borderTopWidth
= parseInt(nodeStyle
.borderTopWidth
, 10) || 0;
749 const leftOffset
= node
.offsetLeft
- node
.scrollLeft
+ borderLeftWidth
;
750 const topOffset
= node
.offsetTop
- node
.scrollTop
+ borderTopWidth
;
752 quad
.p1
.x
+= leftOffset
;
753 quad
.p2
.x
+= leftOffset
;
754 quad
.p3
.x
+= leftOffset
;
755 quad
.p4
.x
+= leftOffset
;
756 quad
.p1
.y
+= topOffset
;
757 quad
.p2
.y
+= topOffset
;
758 quad
.p3
.y
+= topOffset
;
759 quad
.p4
.y
+= topOffset
;
761 node
= node
.offsetParent
;
766 exports
.getUntransformedQuad
= getUntransformedQuad
;
769 * Calculate the total of the node and all of its ancestor's scrollTop and
772 * @param {DOMNode} node
773 * The node for which the absolute scroll offsets should be calculated.
774 * @return {Object} object
775 * An object containing scrollTop and scrollLeft values.
776 * @return {Number} object.scrollLeft
777 * The total scrollLeft values of the node and all of its ancestors.
778 * @return {Number} object.scrollTop
779 * The total scrollTop values of the node and all of its ancestors.
781 function getAbsoluteScrollOffsetsForNode(node
) {
782 const doc
= node
.ownerDocument
;
784 // Our walker will only iterate up to document.body so we start by saving the
785 // scroll values for `document.documentElement`.
786 let scrollTop
= doc
.documentElement
.scrollTop
;
787 let scrollLeft
= doc
.documentElement
.scrollLeft
;
788 const walker
= doc
.createTreeWalker(doc
.body
, NodeFilter
.SHOW_ELEMENT
);
789 walker
.currentNode
= node
;
790 let currentNode
= walker
.currentNode
;
792 // Iterate from `node` up the tree to `document.body` adding scroll offsets
794 while (currentNode
) {
795 const nodeScrollTop
= currentNode
.scrollTop
;
796 const nodeScrollLeft
= currentNode
.scrollLeft
;
798 if (nodeScrollTop
|| nodeScrollLeft
) {
799 scrollTop
+= nodeScrollTop
;
800 scrollLeft
+= nodeScrollLeft
;
803 currentNode
= walker
.parentNode();
811 exports
.getAbsoluteScrollOffsetsForNode
= getAbsoluteScrollOffsetsForNode
;
814 * Check if the provided node is a <frame> or <iframe> element.
816 * @param {DOMNode} node
819 function isFrame(node
) {
820 const className
= ChromeUtils
.getClassName(node
);
821 return className
== "HTMLIFrameElement" || className
== "HTMLFrameElement";
825 * Check if the provided node is representing a remote <browser> element.
827 * @param {DOMNode} node
830 function isRemoteBrowserElement(node
) {
832 ChromeUtils
.getClassName(node
) == "XULFrameElement" &&
833 !node
.childNodes
.length
&&
834 node
.getAttribute("remote") == "true"
837 exports
.isRemoteBrowserElement
= isRemoteBrowserElement
;
840 * Check if the provided node is representing a remote frame.
842 * - In the context of the browser toolbox, a remote frame can be the <browser remote>
843 * element found inside each tab.
844 * - In the context of the content toolbox, a remote frame can be a <iframe> that contains
845 * a different origin document.
847 * @param {DOMNode} node
850 function isRemoteFrame(node
) {
852 return node
.frameLoader
?.isRemoteFrame
;
855 if (isRemoteBrowserElement(node
)) {
861 exports
.isRemoteFrame
= isRemoteFrame
;
864 * Check if the provided node is representing a frame that has its own dedicated child target.
866 * @param {BrowsingContextTargetActor} targetActor
867 * @param {DOMNode} node
870 function isFrameWithChildTarget(targetActor
, node
) {
871 // If the iframe is blocked because of CSP, it won't have a document (and no associated targets)
872 if (isFrameBlockedByCSP(node
)) {
876 return isRemoteFrame(node
) || (isFrame(node
) && targetActor
.ignoreSubFrames
);
879 exports
.isFrameWithChildTarget
= isFrameWithChildTarget
;
882 * Check if the provided node is representing a frame that is blocked by CSP.
884 * @param {DOMNode} node
887 function isFrameBlockedByCSP(node
) {
888 if (!isFrame(node
)) {
898 uri
= lazy
.NetUtil
.newURI(node
.src
);
903 const res
= node
.ownerDocument
.csp
.shouldLoad(
904 Ci
.nsIContentPolicy
.TYPE_SUBDOCUMENT
,
905 null, // nsICSPEventListener
908 null, // aOriginalURIIfRedirect
909 false // aSendViolationReports
912 return res
!== Ci
.nsIContentPolicy
.ACCEPT
;
915 exports
.isFrameBlockedByCSP
= isFrameBlockedByCSP
;