feat(chromium): always inject paint-callback polyfill to cover waitDelay users

This commit is contained in:
Julien Neuhart
2026-04-24 14:30:45 +02:00
parent ce9c48b2b0
commit 200334197f
3 changed files with 32 additions and 19 deletions

View File

@@ -308,7 +308,6 @@ func (b *chromiumBrowser) Healthy(logger *slog.Logger) bool {
func (b *chromiumBrowser) pdf(ctx context.Context, logger *slog.Logger, url, outputPath string, options PdfOptions) error {
// Note: no error wrapping because it leaks on errors we want to display to
// the end user.
installPaintPolyfill := options.WaitForExpression != "" || options.WaitForSelector != ""
return b.do(ctx, logger, url, options.Options, chromedp.Tasks{
network.Enable(),
fetch.Enable(),
@@ -318,7 +317,7 @@ func (b *chromiumBrowser) pdf(ctx context.Context, logger *slog.Logger, url, out
disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript),
setCookiesActionFunc(logger, options.Cookies),
userAgentOverride(logger, options.UserAgent),
injectPaintCallbacksPolyfillActionFunc(logger, installPaintPolyfill),
injectPaintCallbacksPolyfillActionFunc(logger),
navigateActionFunc(logger, url, options.SkipNetworkIdleEvent, options.SkipNetworkAlmostIdleEvent),
hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, options.PrintBackground),
forceExactColorsActionFunc(logger, options.PrintBackground),
@@ -336,7 +335,6 @@ func (b *chromiumBrowser) pdf(ctx context.Context, logger *slog.Logger, url, out
func (b *chromiumBrowser) screenshot(ctx context.Context, logger *slog.Logger, url, outputPath string, options ScreenshotOptions) error {
// Note: no error wrapping because it leaks on errors we want to display to
// the end user.
installPaintPolyfill := options.WaitForExpression != "" || options.WaitForSelector != ""
return b.do(ctx, logger, url, options.Options, chromedp.Tasks{
network.Enable(),
fetch.Enable(),
@@ -346,7 +344,7 @@ func (b *chromiumBrowser) screenshot(ctx context.Context, logger *slog.Logger, u
disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript),
setCookiesActionFunc(logger, options.Cookies),
userAgentOverride(logger, options.UserAgent),
injectPaintCallbacksPolyfillActionFunc(logger, installPaintPolyfill),
injectPaintCallbacksPolyfillActionFunc(logger),
navigateActionFunc(logger, url, options.SkipNetworkIdleEvent, options.SkipNetworkAlmostIdleEvent),
hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, true),
forceExactColorsActionFunc(logger, true),

View File

@@ -124,15 +124,14 @@ const paintCallbacksPolyfill = `
// injectPaintCallbacksPolyfillActionFunc installs the
// [paintCallbacksPolyfill] via [page.AddScriptToEvaluateOnNewDocument]
// so that it runs before any user script on the navigated page. When
// install is false the action is a no-op and the page keeps the native
// rAF, ResizeObserver, and IntersectionObserver implementations.
func injectPaintCallbacksPolyfillActionFunc(logger *slog.Logger, install bool) chromedp.ActionFunc {
// so that it runs before any user script on the navigated page. The
// shim is installed unconditionally: the native rAF, ResizeObserver,
// and IntersectionObserver APIs do not fire during Chromium's
// headless-print render, so replacing them with timer-backed
// implementations either restores the behavior the page expects or has
// no effect when the page never calls them.
func injectPaintCallbacksPolyfillActionFunc(logger *slog.Logger) chromedp.ActionFunc {
return func(ctx context.Context) error {
if !install {
logger.DebugContext(ctx, "paint-callbacks polyfill not requested")
return nil
}
logger.DebugContext(ctx, "inject paint-callbacks polyfill")
_, err := page.AddScriptToEvaluateOnNewDocument(paintCallbacksPolyfill).Do(ctx)
if err != nil {