-
-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix fullPage
rendering and loading
#65
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,4 +1,4 @@ | ||||||
/* global document */ | ||||||
/* global document, window */ | ||||||
'use strict'; | ||||||
const {promisify} = require('util'); | ||||||
const fs = require('fs'); | ||||||
|
@@ -119,8 +119,6 @@ const parseCookie = (url, cookie) => { | |||||
return returnValue; | ||||||
}; | ||||||
|
||||||
const imagesHaveLoaded = () => [...document.images].map(element => element.complete); | ||||||
|
||||||
const captureWebsite = async (input, options) => { | ||||||
options = { | ||||||
inputType: 'url', | ||||||
|
@@ -319,36 +317,48 @@ const captureWebsite = async (input, options) => { | |||||
} | ||||||
|
||||||
if (screenshotOptions.fullPage) { | ||||||
// Get the height of the rendered page | ||||||
const bodyHandle = await page.$('body'); | ||||||
const bodyBoundingHeight = await bodyHandle.boundingBox(); | ||||||
await bodyHandle.dispose(); | ||||||
|
||||||
// Scroll one viewport at a time, pausing to let content load | ||||||
const viewportHeight = viewportOptions.height; | ||||||
let viewportIncrement = 0; | ||||||
while (viewportIncrement + viewportHeight < bodyBoundingHeight) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This loop was inaccessible before due to |
||||||
const navigationPromise = page.waitForNavigation({waitUntil: 'networkidle0'}); | ||||||
/* eslint-disable no-await-in-loop */ | ||||||
await page.evaluate(_viewportHeight => { | ||||||
/* eslint-disable no-undef */ | ||||||
window.scrollBy(0, _viewportHeight); | ||||||
/* eslint-enable no-undef */ | ||||||
}, viewportHeight); | ||||||
await navigationPromise; | ||||||
/* eslint-enable no-await-in-loop */ | ||||||
viewportIncrement += viewportHeight; | ||||||
const idleCallbackToken = 'CAPTURE_WEBSITE_SCROLL_READY'; | ||||||
const autoScroll = async () => { | ||||||
// Scroll and check if done | ||||||
const isBottom = await page.evaluate(idleCallbackToken => { | ||||||
window[idleCallbackToken] = false; | ||||||
window.scrollBy(0, window.innerHeight); | ||||||
|
||||||
// Wait for idle event loop to ensure client-side rendering is complete | ||||||
window.requestIdleCallback(() => { | ||||||
window[idleCallbackToken] = true; | ||||||
}, {timeout: 100}); | ||||||
|
||||||
return window.scrollY >= document.body.clientHeight - window.innerHeight; | ||||||
}, idleCallbackToken); | ||||||
|
||||||
// Once the event loop is idle, ensure images are done loading | ||||||
await page.waitForFunction(`window.${idleCallbackToken} === true`); | ||||||
await page.waitForFunction(() => [...document.images].every(element => element.complete)); | ||||||
|
||||||
return !isBottom; | ||||||
}; | ||||||
|
||||||
while (await autoScroll()) { /* eslint-disable-line no-await-in-loop */ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this have a timeout? Otherwise, it would just go on forever on sites with infinite auto-loading. |
||||||
// noop | ||||||
} | ||||||
|
||||||
// Scroll back to top | ||||||
await page.evaluate(_ => { | ||||||
/* eslint-disable no-undef */ | ||||||
window.scrollTo(0, 0); | ||||||
/* eslint-enable no-undef */ | ||||||
// Workaround for chromium height limitations: https://bugs.chromium.org/p/chromium/issues/detail?id=770769#c12 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
const height = await page.evaluate(() => document.documentElement.scrollHeight); | ||||||
const maxTextureSize = await page.evaluate(() => { | ||||||
const canvas = document.createElement('canvas'); | ||||||
const webGL = canvas.getContext('webgl'); | ||||||
return webGL.getParameter(webGL.MAX_TEXTURE_SIZE); | ||||||
}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Note: The same issue can technically happen horizontally. If there's a use case out there, we should scroll and scale in both dimensions ( maybe a follow-up PR? ). |
||||||
|
||||||
// Some extra delay to let images load | ||||||
await page.waitForFunction(imagesHaveLoaded, {timeout: timeoutInSeconds}); | ||||||
// Adjust screenshot to fit entire page within max canvas dimensions | ||||||
const maxScaleFactor = Number.parseFloat((maxTextureSize / height).toFixed(2)); | ||||||
if (viewportOptions.deviceScaleFactor > maxScaleFactor) { | ||||||
await page.setViewport({ | ||||||
...viewportOptions, | ||||||
deviceScaleFactor: maxScaleFactor | ||||||
}); | ||||||
} | ||||||
} | ||||||
|
||||||
const buffer = await page.screenshot(screenshotOptions); | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was returning an array of
true/false
before; alwaystruthy
and thus never waited.