From 0b223b9036dad2305ac5a21425a0455731c32d2c Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Tue, 27 Dec 2022 16:59:34 -0800 Subject: [PATCH] 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. --- packages/playwright-core/src/server/dom.ts | 2 +- .../src/server/injected/injectedScript.ts | 7 ++- tests/library/hit-target.spec.ts | 48 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/packages/playwright-core/src/server/dom.ts b/packages/playwright-core/src/server/dom.ts index be472cb86a..c8cdaa7867 100644 --- a/packages/playwright-core/src/server/dom.ts +++ b/packages/playwright-core/src/server/dom.ts @@ -877,7 +877,7 @@ export class ElementHandle extends js.JSHandle { return { framePoint: undefined }; } // 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 }); frame = frame.parentFrame()!; } diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index 4f2ec64745..42b4021e80 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -486,7 +486,7 @@ export class InjectedScript { 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) return 'error:notconnected'; const defaultView = iframe.ownerDocument.defaultView; @@ -495,7 +495,10 @@ export class InjectedScript { return 'transformed'; } 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 { diff --git a/tests/library/hit-target.spec.ts b/tests/library/hit-target.spec.ts index 2564efc6f5..9e5362ab18 100644 --- a/tests/library/hit-target.spec.ts +++ b/tests/library/hit-target.spec.ts @@ -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); expect(error.message).toContain('
Overlay
intercepts pointer events'); }); + +it('should click in iframe with padding', async ({ page }) => { + await page.setContent(` + + + `); + 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(` + + + `); + const locator = page.frameLocator('iframe').locator('#target'); + await locator.click(); + expect(await page.evaluate('window._clicked')).toBe(true); +});