Skip to content

Commit

Permalink
fix(page): fix page.#scrollIntoViewIfNeeded method (#8631)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
abozhilov committed Jul 8, 2022
1 parent 1de0383 commit b47f066
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 35 deletions.
68 changes: 35 additions & 33 deletions src/common/ElementHandle.ts
Expand Up @@ -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(
Expand Down
5 changes: 4 additions & 1 deletion test/src/click.spec.ts
Expand Up @@ -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).
Expand Down
2 changes: 1 addition & 1 deletion test/src/elementhandle.spec.ts
Expand Up @@ -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');
Expand Down

0 comments on commit b47f066

Please sign in to comment.