fix(hit target): account for iframes with padding (#19732)
Padding on iframes moves the `documentElement` inside the iframe, so we should account for it when converting coordinates between frames. Fixes #19613.
This commit is contained in:
parent
3334d89ad7
commit
0b223b9036
|
|
@ -877,7 +877,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
return { framePoint: undefined };
|
return { framePoint: undefined };
|
||||||
}
|
}
|
||||||
// Translate from viewport coordinates to frame coordinates.
|
// Translate from viewport coordinates to frame coordinates.
|
||||||
const pointInFrame = { x: point.x - box.x - style.borderLeft, y: point.y - box.y - style.borderTop };
|
const pointInFrame = { x: point.x - box.x - style.left, y: point.y - box.y - style.top };
|
||||||
data.push({ frame, frameElement, pointInFrame });
|
data.push({ frame, frameElement, pointInFrame });
|
||||||
frame = frame.parentFrame()!;
|
frame = frame.parentFrame()!;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -486,7 +486,7 @@ export class InjectedScript {
|
||||||
return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) };
|
return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) };
|
||||||
}
|
}
|
||||||
|
|
||||||
describeIFrameStyle(iframe: Element): 'error:notconnected' | 'transformed' | { borderLeft: number, borderTop: number } {
|
describeIFrameStyle(iframe: Element): 'error:notconnected' | 'transformed' | { left: number, top: number } {
|
||||||
if (!iframe.ownerDocument || !iframe.ownerDocument.defaultView)
|
if (!iframe.ownerDocument || !iframe.ownerDocument.defaultView)
|
||||||
return 'error:notconnected';
|
return 'error:notconnected';
|
||||||
const defaultView = iframe.ownerDocument.defaultView;
|
const defaultView = iframe.ownerDocument.defaultView;
|
||||||
|
|
@ -495,7 +495,10 @@ export class InjectedScript {
|
||||||
return 'transformed';
|
return 'transformed';
|
||||||
}
|
}
|
||||||
const iframeStyle = defaultView.getComputedStyle(iframe);
|
const iframeStyle = defaultView.getComputedStyle(iframe);
|
||||||
return { borderLeft: parseInt(iframeStyle.borderLeftWidth || '', 10), borderTop: parseInt(iframeStyle.borderTopWidth || '', 10) };
|
return {
|
||||||
|
left: parseInt(iframeStyle.borderLeftWidth || '', 10) + parseInt(iframeStyle.paddingLeft || '', 10),
|
||||||
|
top: parseInt(iframeStyle.borderTopWidth || '', 10) + parseInt(iframeStyle.paddingTop || '', 10),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
retarget(node: Node, behavior: 'none' | 'follow-label' | 'no-follow-label' | 'button-link'): Element | null {
|
retarget(node: Node, behavior: 'none' | 'follow-label' | 'no-follow-label' | 'button-link'): Element | null {
|
||||||
|
|
|
||||||
|
|
@ -394,3 +394,51 @@ it('should detect overlayed element in a transformed iframe', async ({ page }) =
|
||||||
const error = await locator.click({ timeout: 2000 }).catch(e => e);
|
const error = await locator.click({ timeout: 2000 }).catch(e => e);
|
||||||
expect(error.message).toContain('<section>Overlay</section> intercepts pointer events');
|
expect(error.message).toContain('<section>Overlay</section> intercepts pointer events');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should click in iframe with padding', async ({ page }) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<style>
|
||||||
|
body, html, iframe { margin: 0; padding: 0; border: none; box-sizing: border-box; }
|
||||||
|
iframe { background: gray; width: 200px; height: 200px; padding-top: 100px; }
|
||||||
|
</style>
|
||||||
|
<iframe srcdoc="
|
||||||
|
<style>
|
||||||
|
body, html { margin: 0; padding: 0; }
|
||||||
|
div { height: 100px; }
|
||||||
|
</style>
|
||||||
|
<div>Non-target</div>
|
||||||
|
<div id=target>Target</div>
|
||||||
|
<div>Non-target</div>
|
||||||
|
<script>
|
||||||
|
document.querySelector('#target').addEventListener('click', () => window.top._clicked = true);
|
||||||
|
</script>
|
||||||
|
"></iframe>
|
||||||
|
`);
|
||||||
|
const locator = page.frameLocator('iframe').locator('#target');
|
||||||
|
await locator.click();
|
||||||
|
expect(await page.evaluate('window._clicked')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should click in iframe with padding 2', async ({ page }) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<style>
|
||||||
|
body, html, iframe { margin: 0; padding: 0; border: none; box-sizing: content-box; }
|
||||||
|
iframe { background: gray; width: 200px; height: 200px; padding-top: 100px; }
|
||||||
|
</style>
|
||||||
|
<iframe srcdoc="
|
||||||
|
<style>
|
||||||
|
body, html { margin: 0; padding: 0; }
|
||||||
|
div { height: 100px; }
|
||||||
|
</style>
|
||||||
|
<div>Non-target</div>
|
||||||
|
<div id=target>Target</div>
|
||||||
|
<div>Non-target</div>
|
||||||
|
<script>
|
||||||
|
document.querySelector('#target').addEventListener('click', () => window.top._clicked = true);
|
||||||
|
</script>
|
||||||
|
"></iframe>
|
||||||
|
`);
|
||||||
|
const locator = page.frameLocator('iframe').locator('#target');
|
||||||
|
await locator.click();
|
||||||
|
expect(await page.evaluate('window._clicked')).toBe(true);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue