|
745 | 745 | return '/' + parts.join('/'); |
746 | 746 | } |
747 | 747 |
|
| 748 | + /** |
| 749 | + * Retrieves all child container elements from the specified node |
| 750 | + * |
| 751 | + * This function is used to get child elements when traversing the DOM tree, |
| 752 | + * supporting multiple container types: |
| 753 | + * - Child elements of regular DOM elements |
| 754 | + * - Child elements of Shadow DOM |
| 755 | + * - Body element of iframe internal documents |
| 756 | + * |
| 757 | + * @param {Node|ShadowRoot} node - The node to get child containers from, can be a regular DOM node or Shadow Root |
| 758 | + * @returns {Array<Element>} Returns an array containing all child container elements |
| 759 | + */ |
| 760 | + function getChildContainers(node) { |
| 761 | + const out = []; |
| 762 | + |
| 763 | + // Handle Shadow Root nodes |
| 764 | + if (node instanceof ShadowRoot) { |
| 765 | + out.push(...Array.from(node.children)); |
| 766 | + } |
| 767 | + // Handle regular DOM element nodes |
| 768 | + else if (node && node.nodeType === Node.ELEMENT_NODE) { |
| 769 | + // Add all direct child elements |
| 770 | + out.push(...Array.from(node.children)); |
| 771 | + |
| 772 | + // If the element has a Shadow Root, add it to the container list |
| 773 | + if (node.shadowRoot instanceof ShadowRoot) out.push(node.shadowRoot); |
| 774 | + |
| 775 | + // Special handling for iframe elements, attempt to get the body of their internal document |
| 776 | + if (node.tagName?.toLowerCase() === 'iframe') { |
| 777 | + try { |
| 778 | + const doc = node.contentDocument; |
| 779 | + if (doc?.body) out.push(doc.body); |
| 780 | + } catch (_) { |
| 781 | + /* Ignore errors when cross-origin iframe access is blocked */ |
| 782 | + } |
| 783 | + } |
| 784 | + } |
| 785 | + |
| 786 | + return out; |
| 787 | + } |
| 788 | + |
748 | 789 | /** |
749 | 790 | * Gathers comprehensive information about a DOM element. |
750 | 791 | * |
|
980 | 1021 | * @returns {object | null} A tree node object, or `null` if the element and its descendants are not relevant. |
981 | 1022 | */ |
982 | 1023 | function buildTree(elemObj, wasParentHighlighted = false) { |
983 | | - // 1) get element info |
984 | | - const elemInfo = getElementInfo(elemObj, wasParentHighlighted); |
| 1024 | + // If it is a ShadowRoot, use host as element info; otherwise use the element itself |
| 1025 | + const infoTarget = (elemObj instanceof ShadowRoot) ? elemObj.host : elemObj; |
| 1026 | + |
| 1027 | + // get element info |
| 1028 | + const elemInfo = getElementInfo(infoTarget, wasParentHighlighted); |
985 | 1029 |
|
986 | | - // 2) check node satisfies highlight condition |
987 | | - const isCurNodeHighlighted = handleHighlighting(elemInfo, elemObj, wasParentHighlighted) |
| 1030 | + // Highlight check |
| 1031 | + const isCurNodeHighlighted = elemInfo ? handleHighlighting(elemInfo, infoTarget, wasParentHighlighted) : false; |
988 | 1032 | const isParentHighlighted = wasParentHighlighted || isCurNodeHighlighted; |
989 | 1033 |
|
990 | | - // 3) recursively build structured dom tree, with 'isParentHighlighted' state |
| 1034 | + // Recursively process “container” child nodes: Element children, shadowRoot, and same-origin iframe |
991 | 1035 | const children = []; |
992 | | - Array.from(elemObj.children).forEach(child => { |
| 1036 | + for (const child of getChildContainers(elemObj)) { |
993 | 1037 | const subtree = buildTree(child, isParentHighlighted); |
994 | 1038 | if (subtree) children.push(subtree); |
995 | | - }); |
| 1039 | + } |
996 | 1040 |
|
997 | | - // 4) highlight filter |
| 1041 | + // highlight filter |
998 | 1042 | if (isCurNodeHighlighted) { |
999 | | - highlightIdMap[elemInfo.highlightIndex] = elemInfo; // map highlightIndex to element info |
1000 | | - return {node: elemInfo, children}; // keep info if is highlightable |
| 1043 | + highlightIdMap[elemInfo.highlightIndex] = elemInfo; // map highlightIndex to element info |
| 1044 | + return {node: elemInfo, children}; |
1001 | 1045 | } else if (children.length > 0) { |
1002 | | - return {node: null, children}; // child node is highlightable |
1003 | | - } else { |
1004 | | - return null; // skip |
| 1046 | + return {node: null, children}; // child node is highlightable |
1005 | 1047 | } |
| 1048 | + return null; |
1006 | 1049 | } |
1007 | 1050 |
|
1008 | 1051 | // ============================= Main Function ============================= |
|
0 commit comments