From 0fa20d5d1e51015b3faa20183165043c08de2548 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 8 Aug 2022 16:05:09 -0700 Subject: [PATCH] fix(click): make it work for display:contents elements (#16356) After protocol fixes in all browsers, we can now scroll and click display:contents elements. The only problem is that `elementsFromPoint()` misbehaves in Chromium and Firefox, so we need a workaround. Hopefully, it will be fixed upstream - shadow dom spec folks think "it becomes a real compatibility concern". This needs Chromium 105 roll. --- .../src/server/injected/injectedScript.ts | 14 ++++++- .../elementhandle-scroll-into-view.spec.ts | 8 +++- tests/page/page-click-scroll.spec.ts | 42 ++++++++++++++++++- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index 371aa6ec51..ad016c5491 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -870,9 +870,19 @@ export class InjectedScript { let container: Document | ShadowRoot | null = document; let element: Element | undefined; while (container) { - // elementFromPoint works incorrectly in Chromium (http://crbug.com/1188919), - // so we use elementsFromPoint instead. + // All browsers have different behavior around elementFromPoint and elementsFromPoint. + // https://github.com/w3c/csswg-drafts/issues/556 + // http://crbug.com/1188919 const elements: Element[] = container.elementsFromPoint(x, y); + const singleElement = container.elementFromPoint(x, y); + if (singleElement && elements[0] && parentElementOrShadowHost(singleElement) === elements[0]) { + const style = document.defaultView?.getComputedStyle(singleElement); + if (style?.display === 'contents') { + // Workaround a case where elementsFromPoint misses the inner-most element with display:contents. + // https://bugs.chromium.org/p/chromium/issues/detail?id=1342092 + elements.unshift(singleElement); + } + } const innerElement = elements[0] as Element | undefined; if (!innerElement || element === innerElement) break; diff --git a/tests/page/elementhandle-scroll-into-view.spec.ts b/tests/page/elementhandle-scroll-into-view.spec.ts index 918afebbe0..70705b05da 100644 --- a/tests/page/elementhandle-scroll-into-view.spec.ts +++ b/tests/page/elementhandle-scroll-into-view.spec.ts @@ -60,10 +60,16 @@ it('should wait for display:none to become visible', async ({ page, server }) => await testWaiting(page, div => div.style.display = 'block'); }); -it.fixme('should scroll display:contents into view', async ({ page, server }) => { +it('should scroll display:contents into view', async ({ page, browserName, browserMajorVersion }) => { + it.skip(browserName === 'chromium' && browserMajorVersion < 105, 'Needs https://chromium-review.googlesource.com/c/chromium/src/+/3758670'); it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/15034' }); await page.setContent(` +
diff --git a/tests/page/page-click-scroll.spec.ts b/tests/page/page-click-scroll.spec.ts index c052b6c548..d0207a857a 100644 --- a/tests/page/page-click-scroll.spec.ts +++ b/tests/page/page-click-scroll.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { test as it } from './pageTest'; +import { expect, test as it } from './pageTest'; it('should not hit scroll bar', async ({ page, browserName, platform }) => { it.fixme(browserName === 'webkit' && platform === 'darwin'); @@ -37,3 +37,43 @@ it('should not hit scroll bar', async ({ page, browserName, platform }) => { `); await page.click('text=Story', { timeout: 2000 }); }); + +it('should scroll into view display:contents', async ({ page, browserName, browserMajorVersion }) => { + it.skip(browserName === 'chromium' && browserMajorVersion < 105, 'Needs https://chromium-review.googlesource.com/c/chromium/src/+/3758670'); + + await page.setContent(` +
filler
+
+ Example text, and button here: + +
+ `); + await page.click('text=click me', { timeout: 5000 }); + expect(await page.evaluate('window._clicked')).toBe(true); +}); + +it('should scroll into view display:contents with a child', async ({ page, browserName, browserMajorVersion }) => { + it.skip(browserName === 'chromium' && browserMajorVersion < 105, 'Needs https://chromium-review.googlesource.com/c/chromium/src/+/3758670'); + + await page.setContent(` +
filler
+ Example text, and button here: + + `); + await page.click('text=click me', { timeout: 5000 }); + expect(await page.evaluate('window._clicked')).toBe(true); +}); + +it('should scroll into view display:contents with position', async ({ page, browserName }) => { + it.fixme(browserName === 'chromium', 'DOM.getBoxModel does not work for display:contents'); + + await page.setContent(` +
filler
+
+ Example text, and button here: + +
+ `); + await page.click('text=click me', { position: { x: 5, y: 5 }, timeout: 5000 }); + expect(await page.evaluate('window._clicked')).toBe(true); +});