fix(page): fix page.#scrollIntoViewIfNeeded method (#8631)

This patch fixes page.#scrollIntoViewIfNeeded, so that it works with devtools protocol.
Now it blocks the main thread and waits until the scrolling action finishes in Chrome.
Fallbacks to the old implementation if `DOM.scrollIntoViewIfNeeded` is not supported for Firefox.

Issues: #8627, #1805
This commit is contained in:
Asen Bozhilov 2022-07-08 09:53:45 +03:00 committed by GitHub
parent 1de0383abf
commit b47f066c2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 35 deletions

View File

@ -240,50 +240,52 @@ export class ElementHandle<
async #scrollIntoViewIfNeeded(this: ElementHandle<Element>): Promise<void> {
const error = await this.evaluate(
async (element, pageJavascriptEnabled): Promise<string | false> => {
async (element): Promise<string | undefined> => {
if (!element.isConnected) {
return 'Node is detached from document';
}
if (element.nodeType !== Node.ELEMENT_NODE) {
return 'Node is not of type HTMLElement';
}
// force-scroll if page's javascript is disabled.
if (!pageJavascriptEnabled) {
element.scrollIntoView({
block: 'center',
inline: 'center',
// @ts-expect-error Chrome still supports behavior: instant but
// it's not in the spec so TS shouts We don't want to make this
// breaking change in Puppeteer yet so we'll ignore the line.
behavior: 'instant',
});
return false;
}
const visibleRatio = await new Promise(resolve => {
const observer = new IntersectionObserver(entries => {
resolve(entries[0]!.intersectionRatio);
observer.disconnect();
});
observer.observe(element);
});
if (visibleRatio !== 1.0) {
element.scrollIntoView({
block: 'center',
inline: 'center',
// @ts-expect-error Chrome still supports behavior: instant but
// it's not in the spec so TS shouts We don't want to make this
// breaking change in Puppeteer yet so we'll ignore the line.
behavior: 'instant',
});
}
return false;
},
this.#page.isJavaScriptEnabled()
return;
}
);
if (error) {
throw new Error(error);
}
try {
await this._client.send('DOM.scrollIntoViewIfNeeded', {
objectId: this._remoteObject.objectId,
});
} catch (_err) {
// Fallback to Element.scrollIntoView if DOM.scrollIntoViewIfNeeded is not supported
await this.evaluate(
async (element, pageJavascriptEnabled): Promise<void> => {
const visibleRatio = async () => {
return await new Promise(resolve => {
const observer = new IntersectionObserver(entries => {
resolve(entries[0]!.intersectionRatio);
observer.disconnect();
});
observer.observe(element);
});
};
if (!pageJavascriptEnabled || (await visibleRatio()) !== 1.0) {
element.scrollIntoView({
block: 'center',
inline: 'center',
// @ts-expect-error Chrome still supports behavior: instant but
// it's not in the spec so TS shouts We don't want to make this
// breaking change in Puppeteer yet so we'll ignore the line.
behavior: 'instant',
});
}
},
this.#page.isJavaScriptEnabled()
);
}
}
async #getOOPIFOffsets(

View File

@ -164,7 +164,10 @@ describe('Page.click', function () {
await page.goto(server.PREFIX + '/offscreenbuttons.html');
const messages: any[] = [];
page.on('console', msg => {
return messages.push(msg.text());
if (msg.type() === 'log') {
return messages.push(msg.text());
}
return;
});
for (let i = 0; i < 11; ++i) {
// We might've scrolled to click a button - reset to (0, 0).

View File

@ -203,7 +203,7 @@ describe('ElementHandle specs', function () {
})
).toBe(true);
});
it('should work for TextNodes', async () => {
it('should not work for TextNodes', async () => {
const {page, server} = getTestState();
await page.goto(server.PREFIX + '/input/button.html');