mirror of
https://github.com/microsoft/playwright.git
synced 2025-01-07 11:46:42 +03:00
fix(webkit): click moving targets on windows (#2101)
This commit is contained in:
parent
7e9a8dd402
commit
f6210ae996
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "playwright-core",
|
"name": "playwright-core",
|
||||||
"version": "0.16.0-post",
|
"version": "0.17.0-post",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -262,6 +262,10 @@ export class CRPage implements PageDelegate {
|
|||||||
await this._forAllFrameSessions(frame => frame._setActivityPaused(paused));
|
await this._forAllFrameSessions(frame => frame._setActivityPaused(paused));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rafCountForStablePosition(): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||||
return this._sessionForHandle(handle)._getContentQuads(handle);
|
return this._sessionForHandle(handle)._getContentQuads(handle);
|
||||||
}
|
}
|
||||||
|
@ -455,9 +455,10 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||||||
|
|
||||||
async _waitForDisplayedAtStablePosition(deadline: number): Promise<void> {
|
async _waitForDisplayedAtStablePosition(deadline: number): Promise<void> {
|
||||||
this._page._log(inputLog, 'waiting for element to be displayed and not moving...');
|
this._page._log(inputLog, 'waiting for element to be displayed and not moving...');
|
||||||
const stablePromise = this._evaluateInUtility(({ injected, node }, timeout) => {
|
const rafCount = this._page._delegate.rafCountForStablePosition();
|
||||||
return injected.waitForDisplayedAtStablePosition(node, timeout);
|
const stablePromise = this._evaluateInUtility(({ injected, node }, { rafCount, timeout }) => {
|
||||||
}, helper.timeUntilDeadline(deadline));
|
return injected.waitForDisplayedAtStablePosition(node, rafCount, timeout);
|
||||||
|
}, { rafCount, timeout: helper.timeUntilDeadline(deadline) });
|
||||||
const timeoutMessage = 'element to be displayed and not moving';
|
const timeoutMessage = 'element to be displayed and not moving';
|
||||||
const injectedResult = await helper.waitWithDeadline(stablePromise, timeoutMessage, deadline);
|
const injectedResult = await helper.waitWithDeadline(stablePromise, timeoutMessage, deadline);
|
||||||
handleInjectedResult(injectedResult, timeoutMessage);
|
handleInjectedResult(injectedResult, timeoutMessage);
|
||||||
|
@ -431,6 +431,10 @@ export class FFPage implements PageDelegate {
|
|||||||
async setActivityPaused(paused: boolean): Promise<void> {
|
async setActivityPaused(paused: boolean): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rafCountForStablePosition(): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||||
const result = await this._session.send('Page.getContentQuads', {
|
const result = await this._session.send('Page.getContentQuads', {
|
||||||
frameId: handle._context.frame._id,
|
frameId: handle._context.frame._id,
|
||||||
|
@ -262,7 +262,7 @@ export class Injected {
|
|||||||
input.dispatchEvent(new Event('change', { 'bubbles': true }));
|
input.dispatchEvent(new Event('change', { 'bubbles': true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitForDisplayedAtStablePosition(node: Node, timeout: number): Promise<InjectedResult> {
|
async waitForDisplayedAtStablePosition(node: Node, rafCount: number, timeout: number): Promise<InjectedResult> {
|
||||||
if (!node.isConnected)
|
if (!node.isConnected)
|
||||||
return { status: 'notconnected' };
|
return { status: 'notconnected' };
|
||||||
const element = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;
|
const element = node.nodeType === Node.ELEMENT_NODE ? (node as Element) : node.parentElement;
|
||||||
@ -271,6 +271,8 @@ export class Injected {
|
|||||||
|
|
||||||
let lastRect: types.Rect | undefined;
|
let lastRect: types.Rect | undefined;
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
|
let samePositionCounter = 0;
|
||||||
|
let lastTime = 0;
|
||||||
const result = await this.poll('raf', timeout, (): 'notconnected' | boolean => {
|
const result = await this.poll('raf', timeout, (): 'notconnected' | boolean => {
|
||||||
// First raf happens in the same animation frame as evaluation, so it does not produce
|
// First raf happens in the same animation frame as evaluation, so it does not produce
|
||||||
// any client rect difference compared to synchronous call. We skip the synchronous call
|
// any client rect difference compared to synchronous call. We skip the synchronous call
|
||||||
@ -279,10 +281,22 @@ export class Injected {
|
|||||||
return false;
|
return false;
|
||||||
if (!node.isConnected)
|
if (!node.isConnected)
|
||||||
return 'notconnected';
|
return 'notconnected';
|
||||||
|
|
||||||
|
// Drop frames that are shorter than 16ms - WebKit Win bug.
|
||||||
|
const time = performance.now();
|
||||||
|
if (rafCount > 1 && time - lastTime < 15)
|
||||||
|
return false;
|
||||||
|
lastTime = time;
|
||||||
|
|
||||||
// Note: this logic should be similar to isVisible() to avoid surprises.
|
// Note: this logic should be similar to isVisible() to avoid surprises.
|
||||||
const clientRect = element.getBoundingClientRect();
|
const clientRect = element.getBoundingClientRect();
|
||||||
const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height };
|
const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height };
|
||||||
let isDisplayedAndStable = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height && rect.width > 0 && rect.height > 0;
|
const samePosition = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height && rect.width > 0 && rect.height > 0;
|
||||||
|
if (samePosition)
|
||||||
|
++samePositionCounter;
|
||||||
|
else
|
||||||
|
samePositionCounter = 0;
|
||||||
|
let isDisplayedAndStable = samePositionCounter >= rafCount;
|
||||||
const style = element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element) : undefined;
|
const style = element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element) : undefined;
|
||||||
isDisplayedAndStable = isDisplayedAndStable && (!!style && style.visibility !== 'hidden');
|
isDisplayedAndStable = isDisplayedAndStable && (!!style && style.visibility !== 'hidden');
|
||||||
lastRect = rect;
|
lastRect = rect;
|
||||||
|
@ -70,6 +70,7 @@ export interface PageDelegate {
|
|||||||
getFrameElement(frame: frames.Frame): Promise<dom.ElementHandle>;
|
getFrameElement(frame: frames.Frame): Promise<dom.ElementHandle>;
|
||||||
scrollRectIntoViewIfNeeded(handle: dom.ElementHandle, rect?: types.Rect): Promise<void>;
|
scrollRectIntoViewIfNeeded(handle: dom.ElementHandle, rect?: types.Rect): Promise<void>;
|
||||||
setActivityPaused(paused: boolean): Promise<void>;
|
setActivityPaused(paused: boolean): Promise<void>;
|
||||||
|
rafCountForStablePosition(): number;
|
||||||
|
|
||||||
getAccessibilityTree(needle?: dom.ElementHandle): Promise<{tree: accessibility.AXNode, needle: accessibility.AXNode | null}>;
|
getAccessibilityTree(needle?: dom.ElementHandle): Promise<{tree: accessibility.AXNode, needle: accessibility.AXNode | null}>;
|
||||||
pdf?: (options?: types.PDFOptions) => Promise<Buffer>;
|
pdf?: (options?: types.PDFOptions) => Promise<Buffer>;
|
||||||
|
@ -764,6 +764,10 @@ export class WKPage implements PageDelegate {
|
|||||||
async setActivityPaused(paused: boolean): Promise<void> {
|
async setActivityPaused(paused: boolean): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rafCountForStablePosition(): number {
|
||||||
|
return process.platform === 'win32' ? 5 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||||
const result = await this._session.send('DOM.getContentQuads', {
|
const result = await this._session.send('DOM.getContentQuads', {
|
||||||
objectId: toRemoteObject(handle).objectId!
|
objectId: toRemoteObject(handle).objectId!
|
||||||
|
@ -406,7 +406,7 @@ describe('Page.click', function() {
|
|||||||
await context.close();
|
await context.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.fail(WEBKIT && WIN)('should wait for stable position', async({page, server}) => {
|
it('should wait for stable position', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/button.html');
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
await page.$eval('button', button => {
|
await page.$eval('button', button => {
|
||||||
button.style.transition = 'margin 500ms linear 0s';
|
button.style.transition = 'margin 500ms linear 0s';
|
||||||
@ -424,7 +424,7 @@ describe('Page.click', function() {
|
|||||||
expect(await page.evaluate(() => pageX)).toBe(300);
|
expect(await page.evaluate(() => pageX)).toBe(300);
|
||||||
expect(await page.evaluate(() => pageY)).toBe(10);
|
expect(await page.evaluate(() => pageY)).toBe(10);
|
||||||
});
|
});
|
||||||
it.fail(WEBKIT && WIN)('should timeout waiting for stable position', async({page, server}) => {
|
it('should timeout waiting for stable position', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/button.html');
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
const button = await page.$('button');
|
const button = await page.$('button');
|
||||||
await button.evaluate(button => {
|
await button.evaluate(button => {
|
||||||
|
Loading…
Reference in New Issue
Block a user