Skip to content

Commit 1a8cfbc

Browse files
committed
feat: revamp iframe shadow selector logic
1 parent 83afef7 commit 1a8cfbc

File tree

1 file changed

+72
-54
lines changed

1 file changed

+72
-54
lines changed

server/src/workflow-management/selector.ts

Lines changed: 72 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ import logger from "../logger";
55

66
type Workflow = WorkflowFile["workflow"];
77

8+
type ShadowBoundary = {
9+
type: 'shadow';
10+
host: HTMLElement;
11+
root: ShadowRoot;
12+
element: HTMLElement;
13+
};
14+
15+
type IframeBoundary = {
16+
type: 'iframe';
17+
frame: HTMLIFrameElement;
18+
document: Document;
19+
element: HTMLElement;
20+
};
21+
22+
type Boundary = ShadowBoundary | IframeBoundary;
23+
824
/**
925
* Checks the basic info about an element and returns a {@link BaseActionInfo} object.
1026
* If the element is not found, returns undefined.
@@ -762,22 +778,6 @@ export const getSelectors = async (page: Page, coordinates: Coordinates) => {
762778
One,
763779
}
764780

765-
type ShadowBoundary = {
766-
type: 'shadow';
767-
host: HTMLElement;
768-
root: ShadowRoot;
769-
element: HTMLElement;
770-
};
771-
772-
type IframeBoundary = {
773-
type: 'iframe';
774-
frame: HTMLIFrameElement;
775-
document: Document;
776-
element: HTMLElement;
777-
};
778-
779-
type Boundary = ShadowBoundary | IframeBoundary;
780-
781781
type Options = {
782782
root: Element;
783783
idName: (name: string) => boolean;
@@ -1547,14 +1547,6 @@ interface SelectorResult {
15471547
*/
15481548

15491549
export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates, listSelector: string): Promise<SelectorResult> => {
1550-
interface DOMContext {
1551-
type: 'iframe' | 'shadow';
1552-
element: HTMLElement;
1553-
container: HTMLIFrameElement | ShadowRoot;
1554-
host?: HTMLElement;
1555-
document?: Document;
1556-
}
1557-
15581550
try {
15591551
if (!listSelector) {
15601552
const selectors = await page.evaluate(({ x, y }: { x: number, y: number }) => {
@@ -1648,8 +1640,8 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
16481640
}
16491641

16501642

1651-
function getContextPath(element: HTMLElement): DOMContext[] {
1652-
const path: DOMContext[] = [];
1643+
function getContextPath(element: HTMLElement): Boundary[] {
1644+
const path: Boundary[] = [];
16531645
let current = element;
16541646
let depth = 0;
16551647
const MAX_DEPTH = 4;
@@ -1660,9 +1652,9 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
16601652
if (rootNode instanceof ShadowRoot) {
16611653
path.unshift({
16621654
type: 'shadow',
1663-
element: current,
1664-
container: rootNode,
1665-
host: rootNode.host as HTMLElement
1655+
host: rootNode.host as HTMLElement,
1656+
root: rootNode,
1657+
element: current
16661658
});
16671659
current = rootNode.host as HTMLElement;
16681660
depth++;
@@ -1674,15 +1666,28 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
16741666
const frameElement = ownerDocument?.defaultView?.frameElement as HTMLIFrameElement;
16751667

16761668
if (frameElement) {
1677-
path.unshift({
1678-
type: 'iframe',
1679-
element: current,
1680-
container: frameElement,
1681-
document: ownerDocument
1682-
});
1683-
current = frameElement;
1684-
depth++;
1685-
continue;
1669+
try {
1670+
// Check if we can access the iframe's origin
1671+
const iframeOrigin = new URL(frameElement.src).origin;
1672+
const currentOrigin = window.location.origin;
1673+
if (iframeOrigin !== currentOrigin) {
1674+
console.warn(`Skipping cross-origin iframe: ${iframeOrigin}`);
1675+
break;
1676+
}
1677+
1678+
path.unshift({
1679+
type: 'iframe',
1680+
frame: frameElement,
1681+
document: ownerDocument,
1682+
element: current
1683+
});
1684+
current = frameElement;
1685+
depth++;
1686+
continue;
1687+
} catch (error) {
1688+
console.warn('Cannot access iframe origin:', error);
1689+
break;
1690+
}
16861691
}
16871692

16881693
break;
@@ -1701,7 +1706,7 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
17011706

17021707
contextPath.forEach((context, index) => {
17031708
const containerSelector = getNonUniqueSelector(
1704-
context.type === 'shadow' ? context.host! : context.container as HTMLElement
1709+
context.type === 'shadow' ? context.host! : context.element as HTMLElement
17051710
);
17061711

17071712
if (index === contextPath.length - 1) {
@@ -1888,8 +1893,8 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
18881893
}
18891894

18901895
// Get complete context path (both iframe and shadow DOM)
1891-
function getContextPath(element: HTMLElement): DOMContext[] {
1892-
const path: DOMContext[] = [];
1896+
function getContextPath(element: HTMLElement): Boundary[] {
1897+
const path: Boundary[] = [];
18931898
let current = element;
18941899
let depth = 0;
18951900
const MAX_DEPTH = 4;
@@ -1900,9 +1905,9 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
19001905
if (rootNode instanceof ShadowRoot) {
19011906
path.unshift({
19021907
type: 'shadow',
1903-
element: current,
1904-
container: rootNode,
1905-
host: rootNode.host as HTMLElement
1908+
host: rootNode.host as HTMLElement,
1909+
root: rootNode,
1910+
element: current
19061911
});
19071912
current = rootNode.host as HTMLElement;
19081913
depth++;
@@ -1914,15 +1919,28 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
19141919
const frameElement = ownerDocument?.defaultView?.frameElement as HTMLIFrameElement;
19151920

19161921
if (frameElement) {
1917-
path.unshift({
1918-
type: 'iframe',
1919-
element: current,
1920-
container: frameElement,
1921-
document: ownerDocument
1922-
});
1923-
current = frameElement;
1924-
depth++;
1925-
continue;
1922+
try {
1923+
// Check if we can access the iframe's origin
1924+
const iframeOrigin = new URL(frameElement.src).origin;
1925+
const currentOrigin = window.location.origin;
1926+
if (iframeOrigin !== currentOrigin) {
1927+
console.warn(`Skipping cross-origin iframe: ${iframeOrigin}`);
1928+
break;
1929+
}
1930+
1931+
path.unshift({
1932+
type: 'iframe',
1933+
frame: frameElement,
1934+
document: ownerDocument,
1935+
element: current
1936+
});
1937+
current = frameElement;
1938+
depth++;
1939+
continue;
1940+
} catch (error) {
1941+
console.warn('Cannot access iframe origin:', error);
1942+
break;
1943+
}
19261944
}
19271945

19281946
break;
@@ -1941,7 +1959,7 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
19411959

19421960
contextPath.forEach((context, index) => {
19431961
const containerSelector = getNonUniqueSelector(
1944-
context.type === 'shadow' ? context.host! : context.container as HTMLElement
1962+
context.type === 'shadow' ? context.host! : context.element as HTMLElement
19451963
);
19461964

19471965
if (index === contextPath.length - 1) {

0 commit comments

Comments
 (0)