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); +});