Skip to content

Commit 08706d5

Browse files
committed
fix(input): climb dom for pointer-events:none targets
1 parent 17039f1 commit 08706d5

File tree

3 files changed

+21
-2
lines changed

3 files changed

+21
-2
lines changed

src/dom.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import * as debug from 'debug';
1718
import * as fs from 'fs';
1819
import * as mime from 'mime';
1920
import * as path from 'path';
@@ -36,6 +37,8 @@ export type ClickOptions = PointerActionOptions & input.MouseClickOptions;
3637

3738
export type MultiClickOptions = PointerActionOptions & input.MouseMultiClickOptions;
3839

40+
const debugInput = debug('pw:input');
41+
3942
export class FrameExecutionContext extends js.ExecutionContext {
4043
readonly frame: frames.Frame;
4144
private _injectedPromise?: Promise<js.JSHandle>;
@@ -112,7 +115,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
112115
}
113116

114117
async _scrollRectIntoViewIfNeeded(rect?: types.Rect): Promise<void> {
118+
debugInput('scrolling into veiw if needed...');
115119
await this._page._delegate.scrollRectIntoViewIfNeeded(this, rect);
120+
debugInput('...done');
116121
}
117122

118123
async scrollIntoViewIfNeeded() {
@@ -155,6 +160,8 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
155160
result.x += point.x / 4;
156161
result.y += point.y / 4;
157162
}
163+
result.x = (result.x * 100 | 0) / 100;
164+
result.y = (result.y * 100 | 0) / 100;
158165
return result;
159166
}
160167

@@ -190,7 +197,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
190197
let restoreModifiers: input.Modifier[] | undefined;
191198
if (options && options.modifiers)
192199
restoreModifiers = await this._page.keyboard._ensureModifiers(options.modifiers);
200+
debugInput('performing input action...');
193201
await action(point);
202+
debugInput('...done');
194203
if (restoreModifiers)
195204
await this._page.keyboard._ensureModifiers(restoreModifiers);
196205
}, options, true);
@@ -356,13 +365,16 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
356365
}
357366

358367
async _waitForDisplayedAtStablePosition(options: types.TimeoutOptions = {}): Promise<void> {
368+
debugInput('waiting for element to be displayed and not moving...');
359369
const stablePromise = this._evaluateInUtility(({ injected, node }, timeout) => {
360370
return injected.waitForDisplayedAtStablePosition(node, timeout);
361371
}, options.timeout || 0);
362372
await helper.waitWithTimeout(stablePromise, 'element to be displayed and not moving', options.timeout || 0);
373+
debugInput('...done');
363374
}
364375

365376
async _waitForHitTargetAt(point: types.Point, options: types.TimeoutOptions = {}): Promise<void> {
377+
debugInput(`waiting for element to receive pointer events at (${point.x},${point.y}) ...`);
366378
const frame = await this.ownerFrame();
367379
if (frame && frame.parentFrame()) {
368380
const element = await frame.frameElement();
@@ -375,7 +387,8 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
375387
const hitTargetPromise = this._evaluateInUtility(({ injected, node }, { timeout, point }) => {
376388
return injected.waitForHitTargetAt(node, timeout, point);
377389
}, { timeout: options.timeout || 0, point });
378-
await helper.waitWithTimeout(hitTargetPromise, 'element to receive mouse events', options.timeout || 0);
390+
await helper.waitWithTimeout(hitTargetPromise, 'element to receive pointer events', options.timeout || 0);
391+
debugInput('...done');
379392
}
380393
}
381394

src/injected/injected.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,9 @@ class Injected {
247247
}
248248

249249
async waitForHitTargetAt(node: Node, timeout: number, point: types.Point) {
250-
const element = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;
250+
let element = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;
251+
while (element && window.getComputedStyle(element).pointerEvents === 'none')
252+
element = element.parentElement;
251253
if (!element)
252254
throw new Error('Element is not attached to the DOM');
253255
const result = await this.poll('raf', timeout, () => {

test/click.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,10 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
483483
expect(await page.evaluate(() => window.result)).toBe('Was not clicked');
484484
});
485485

486+
it('should climb dom for pointer-events:none targets', async({page, server}) => {
487+
await page.goto(server.PREFIX + '/input/pointer-events.html');
488+
await page.click('text=Click target');
489+
});
486490
it('should update modifiers correctly', async({page, server}) => {
487491
await page.goto(server.PREFIX + '/input/button.html');
488492
await page.click('button', { modifiers: ['Shift'] });

0 commit comments

Comments
 (0)