diff --git a/README.md b/README.md index ce69acb16f7c8..5aef5f20cebf7 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,7 @@ const iPhone11 = devices['iPhone 11 Pro']; (async () => { const browser = await webkit.launch(); const context = await browser.newContext({ - viewport: iPhone11.viewport, - userAgent: iPhone11.userAgent, + ...iPhone11, geolocation: { longitude: 12.492507, latitude: 41.889938 }, permissions: ['geolocation'] }); diff --git a/src/dom.ts b/src/dom.ts index 9644c1daf349b..c2128f5a670a2 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import * as debug from 'debug'; import * as fs from 'fs'; import * as mime from 'mime'; import * as path from 'path'; @@ -36,6 +37,8 @@ export type ClickOptions = PointerActionOptions & input.MouseClickOptions; export type MultiClickOptions = PointerActionOptions & input.MouseMultiClickOptions; +const debugInput = debug('pw:input'); + export class FrameExecutionContext extends js.ExecutionContext { readonly frame: frames.Frame; private _injectedPromise?: Promise; @@ -112,7 +115,9 @@ export class ElementHandle extends js.JSHandle { } async _scrollRectIntoViewIfNeeded(rect?: types.Rect): Promise { + debugInput('scrolling into veiw if needed...'); await this._page._delegate.scrollRectIntoViewIfNeeded(this, rect); + debugInput('...done'); } async scrollIntoViewIfNeeded() { @@ -186,11 +191,16 @@ export class ElementHandle extends js.JSHandle { if (!force) await this._waitForHitTargetAt(point, options); + point.x = (point.x * 100 | 0) / 100; + point.y = (point.y * 100 | 0) / 100; + await this._page._frameManager.waitForNavigationsCreatedBy(async () => { let restoreModifiers: input.Modifier[] | undefined; if (options && options.modifiers) restoreModifiers = await this._page.keyboard._ensureModifiers(options.modifiers); + debugInput('performing input action...'); await action(point); + debugInput('...done'); if (restoreModifiers) await this._page.keyboard._ensureModifiers(restoreModifiers); }, options, true); @@ -356,13 +366,16 @@ export class ElementHandle extends js.JSHandle { } async _waitForDisplayedAtStablePosition(options: types.TimeoutOptions = {}): Promise { + debugInput('waiting for element to be displayed and not moving...'); const stablePromise = this._evaluateInUtility(({ injected, node }, timeout) => { return injected.waitForDisplayedAtStablePosition(node, timeout); }, options.timeout || 0); await helper.waitWithTimeout(stablePromise, 'element to be displayed and not moving', options.timeout || 0); + debugInput('...done'); } async _waitForHitTargetAt(point: types.Point, options: types.TimeoutOptions = {}): Promise { + debugInput(`waiting for element to receive pointer events at (${point.x},${point.y}) ...`); const frame = await this.ownerFrame(); if (frame && frame.parentFrame()) { const element = await frame.frameElement(); @@ -375,7 +388,8 @@ export class ElementHandle extends js.JSHandle { const hitTargetPromise = this._evaluateInUtility(({ injected, node }, { timeout, point }) => { return injected.waitForHitTargetAt(node, timeout, point); }, { timeout: options.timeout || 0, point }); - await helper.waitWithTimeout(hitTargetPromise, 'element to receive mouse events', options.timeout || 0); + await helper.waitWithTimeout(hitTargetPromise, 'element to receive pointer events', options.timeout || 0); + debugInput('...done'); } } diff --git a/src/injected/injected.ts b/src/injected/injected.ts index 8a08094ae85c5..12512b2d9eb30 100644 --- a/src/injected/injected.ts +++ b/src/injected/injected.ts @@ -247,7 +247,9 @@ class Injected { } async waitForHitTargetAt(node: Node, timeout: number, point: types.Point) { - const element = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement; + let element = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement; + while (element && window.getComputedStyle(element).pointerEvents === 'none') + element = element.parentElement; if (!element) throw new Error('Element is not attached to the DOM'); const result = await this.poll('raf', timeout, () => { diff --git a/test/click.spec.js b/test/click.spec.js index 005a10edee870..efdff4f3f2312 100644 --- a/test/click.spec.js +++ b/test/click.spec.js @@ -483,6 +483,10 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI expect(await page.evaluate(() => window.result)).toBe('Was not clicked'); }); + it('should climb dom for pointer-events:none targets', async({page, server}) => { + await page.setContent('') + await page.click('text=Click target'); + }); it('should update modifiers correctly', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); await page.click('button', { modifiers: ['Shift'] });