mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
fix(page): intersect content quads with viewport (#4277)
In certain cases inline element children might be positioned outside of viewport. In this case, we should intersect all content quads with viewport before we pick one to click into. Fixes #4274.
This commit is contained in:
parent
20988775bf
commit
5ee21d97e7
@ -189,13 +189,17 @@ class ElementHandle extends JSHandle {
|
|||||||
* @return {!Promise<!{x: number, y: number}>}
|
* @return {!Promise<!{x: number, y: number}>}
|
||||||
*/
|
*/
|
||||||
async _clickablePoint() {
|
async _clickablePoint() {
|
||||||
const result = await this._client.send('DOM.getContentQuads', {
|
const [result, layoutMetrics] = await Promise.all([
|
||||||
objectId: this._remoteObject.objectId
|
this._client.send('DOM.getContentQuads', {
|
||||||
}).catch(debugError);
|
objectId: this._remoteObject.objectId
|
||||||
|
}).catch(debugError),
|
||||||
|
this._client.send('Page.getLayoutMetrics'),
|
||||||
|
]);
|
||||||
if (!result || !result.quads.length)
|
if (!result || !result.quads.length)
|
||||||
throw new Error('Node is either not visible or not an HTMLElement');
|
throw new Error('Node is either not visible or not an HTMLElement');
|
||||||
// Filter out quads that have too small area to click into.
|
// Filter out quads that have too small area to click into.
|
||||||
const quads = result.quads.map(quad => this._fromProtocolQuad(quad)).filter(quad => computeQuadArea(quad) > 1);
|
const {clientWidth, clientHeight} = layoutMetrics.layoutViewport;
|
||||||
|
const quads = result.quads.map(quad => this._fromProtocolQuad(quad)).map(quad => this._intersectQuadWithViewport(quad, clientWidth, clientHeight)).filter(quad => computeQuadArea(quad) > 1);
|
||||||
if (!quads.length)
|
if (!quads.length)
|
||||||
throw new Error('Node is either not visible or not an HTMLElement');
|
throw new Error('Node is either not visible or not an HTMLElement');
|
||||||
// Return the middle point of the first quad.
|
// Return the middle point of the first quad.
|
||||||
@ -234,6 +238,19 @@ class ElementHandle extends JSHandle {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!Array<{x: number, y: number}>} quad
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
* @return {!Array<{x: number, y: number}>}
|
||||||
|
*/
|
||||||
|
_intersectQuadWithViewport(quad, width, height) {
|
||||||
|
return quad.map(point => ({
|
||||||
|
x: Math.min(Math.max(point.x, 0), width),
|
||||||
|
y: Math.min(Math.max(point.y, 0), height),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
async hover() {
|
async hover() {
|
||||||
await this._scrollIntoViewIfNeeded();
|
await this._scrollIntoViewIfNeeded();
|
||||||
const {x, y} = await this._clickablePoint();
|
const {x, y} = await this._clickablePoint();
|
||||||
|
@ -50,6 +50,19 @@ module.exports.addTests = function({testRunner, expect}) {
|
|||||||
]);
|
]);
|
||||||
expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked');
|
expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked');
|
||||||
});
|
});
|
||||||
|
it_fails_ffox('should click when one of inline box children is outside of viewport', async({page, server}) => {
|
||||||
|
await page.setContent(`
|
||||||
|
<style>
|
||||||
|
i {
|
||||||
|
position: absolute;
|
||||||
|
top: -1000px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<span onclick='javascript:window.CLICKED = 42;'><i>woof</i><b>doggo</b></span>
|
||||||
|
`);
|
||||||
|
await page.click('span');
|
||||||
|
expect(await page.evaluate(() => window.CLICKED)).toBe(42);
|
||||||
|
});
|
||||||
it('should select the text by triple clicking', async({page, server}) => {
|
it('should select the text by triple clicking', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||||
await page.focus('textarea');
|
await page.focus('textarea');
|
||||||
|
Loading…
Reference in New Issue
Block a user