mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
feat(chromium): always inject paint-callback polyfill to cover waitDelay users
This commit is contained in:
@@ -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 {
|
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
|
// Note: no error wrapping because it leaks on errors we want to display to
|
||||||
// the end user.
|
// the end user.
|
||||||
installPaintPolyfill := options.WaitForExpression != "" || options.WaitForSelector != ""
|
|
||||||
return b.do(ctx, logger, url, options.Options, chromedp.Tasks{
|
return b.do(ctx, logger, url, options.Options, chromedp.Tasks{
|
||||||
network.Enable(),
|
network.Enable(),
|
||||||
fetch.Enable(),
|
fetch.Enable(),
|
||||||
@@ -318,7 +317,7 @@ func (b *chromiumBrowser) pdf(ctx context.Context, logger *slog.Logger, url, out
|
|||||||
disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript),
|
disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript),
|
||||||
setCookiesActionFunc(logger, options.Cookies),
|
setCookiesActionFunc(logger, options.Cookies),
|
||||||
userAgentOverride(logger, options.UserAgent),
|
userAgentOverride(logger, options.UserAgent),
|
||||||
injectPaintCallbacksPolyfillActionFunc(logger, installPaintPolyfill),
|
injectPaintCallbacksPolyfillActionFunc(logger),
|
||||||
navigateActionFunc(logger, url, options.SkipNetworkIdleEvent, options.SkipNetworkAlmostIdleEvent),
|
navigateActionFunc(logger, url, options.SkipNetworkIdleEvent, options.SkipNetworkAlmostIdleEvent),
|
||||||
hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, options.PrintBackground),
|
hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, options.PrintBackground),
|
||||||
forceExactColorsActionFunc(logger, 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 {
|
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
|
// Note: no error wrapping because it leaks on errors we want to display to
|
||||||
// the end user.
|
// the end user.
|
||||||
installPaintPolyfill := options.WaitForExpression != "" || options.WaitForSelector != ""
|
|
||||||
return b.do(ctx, logger, url, options.Options, chromedp.Tasks{
|
return b.do(ctx, logger, url, options.Options, chromedp.Tasks{
|
||||||
network.Enable(),
|
network.Enable(),
|
||||||
fetch.Enable(),
|
fetch.Enable(),
|
||||||
@@ -346,7 +344,7 @@ func (b *chromiumBrowser) screenshot(ctx context.Context, logger *slog.Logger, u
|
|||||||
disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript),
|
disableJavaScriptActionFunc(logger, b.arguments.disableJavaScript),
|
||||||
setCookiesActionFunc(logger, options.Cookies),
|
setCookiesActionFunc(logger, options.Cookies),
|
||||||
userAgentOverride(logger, options.UserAgent),
|
userAgentOverride(logger, options.UserAgent),
|
||||||
injectPaintCallbacksPolyfillActionFunc(logger, installPaintPolyfill),
|
injectPaintCallbacksPolyfillActionFunc(logger),
|
||||||
navigateActionFunc(logger, url, options.SkipNetworkIdleEvent, options.SkipNetworkAlmostIdleEvent),
|
navigateActionFunc(logger, url, options.SkipNetworkIdleEvent, options.SkipNetworkAlmostIdleEvent),
|
||||||
hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, true),
|
hideDefaultWhiteBackgroundActionFunc(logger, options.OmitBackground, true),
|
||||||
forceExactColorsActionFunc(logger, true),
|
forceExactColorsActionFunc(logger, true),
|
||||||
|
|||||||
@@ -124,15 +124,14 @@ const paintCallbacksPolyfill = `
|
|||||||
|
|
||||||
// injectPaintCallbacksPolyfillActionFunc installs the
|
// injectPaintCallbacksPolyfillActionFunc installs the
|
||||||
// [paintCallbacksPolyfill] via [page.AddScriptToEvaluateOnNewDocument]
|
// [paintCallbacksPolyfill] via [page.AddScriptToEvaluateOnNewDocument]
|
||||||
// so that it runs before any user script on the navigated page. When
|
// so that it runs before any user script on the navigated page. The
|
||||||
// install is false the action is a no-op and the page keeps the native
|
// shim is installed unconditionally: the native rAF, ResizeObserver,
|
||||||
// rAF, ResizeObserver, and IntersectionObserver implementations.
|
// and IntersectionObserver APIs do not fire during Chromium's
|
||||||
func injectPaintCallbacksPolyfillActionFunc(logger *slog.Logger, install bool) chromedp.ActionFunc {
|
// 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 {
|
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")
|
logger.DebugContext(ctx, "inject paint-callbacks polyfill")
|
||||||
_, err := page.AddScriptToEvaluateOnNewDocument(paintCallbacksPolyfill).Do(ctx)
|
_, err := page.AddScriptToEvaluateOnNewDocument(paintCallbacksPolyfill).Do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ Feature: /forms/chromium/convert/html
|
|||||||
Wait delay > 2 seconds or expression window globalVar === 'ready' returns true.
|
Wait delay > 2 seconds or expression window globalVar === 'ready' returns true.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Scenario: POST /forms/chromium/convert/html (paint-callback polyfill fires rAF / ResizeObserver / IntersectionObserver when waitForExpression is set)
|
Scenario: POST /forms/chromium/convert/html (paint-callback polyfill fires rAF / ResizeObserver / IntersectionObserver with waitForExpression)
|
||||||
Given I have a default Gotenberg container
|
Given I have a default Gotenberg container
|
||||||
When I make a "POST" request to Gotenberg at the "/forms/chromium/convert/html" endpoint with the following form data and header(s):
|
When I make a "POST" request to Gotenberg at the "/forms/chromium/convert/html" endpoint with the following form data and header(s):
|
||||||
| files | testdata/paint-callbacks-html/index.html | file |
|
| files | testdata/paint-callbacks-html/index.html | file |
|
||||||
@@ -219,14 +219,30 @@ Feature: /forms/chromium/convert/html
|
|||||||
io-fired
|
io-fired
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Scenario: POST /forms/chromium/convert/html (paint-callback polyfill skipped without a readiness signal)
|
Scenario: POST /forms/chromium/convert/html (paint-callback polyfill fires rAF / ResizeObserver / IntersectionObserver with waitDelay and emulatedMediaType=print)
|
||||||
Given I have a Gotenberg container with the following environment variable(s):
|
Given I have a default Gotenberg container
|
||||||
| LOG_LEVEL | debug |
|
|
||||||
When I make a "POST" request to Gotenberg at the "/forms/chromium/convert/html" endpoint with the following form data and header(s):
|
When I make a "POST" request to Gotenberg at the "/forms/chromium/convert/html" endpoint with the following form data and header(s):
|
||||||
| files | testdata/page-1-html/index.html | file |
|
| files | testdata/paint-callbacks-html/index.html | file |
|
||||||
|
| waitDelay | 3s | field |
|
||||||
|
| emulatedMediaType | print | field |
|
||||||
|
| Gotenberg-Output-Filename | foo | header |
|
||||||
Then the response status code should be 200
|
Then the response status code should be 200
|
||||||
Then the Gotenberg container should log the following entries:
|
Then the response header "Content-Type" should be "application/pdf"
|
||||||
| paint-callbacks polyfill not requested |
|
Then there should be 1 PDF(s) in the response
|
||||||
|
Then there should be the following file(s) in the response:
|
||||||
|
| foo.pdf |
|
||||||
|
Then the "foo.pdf" PDF should have the following content at page 1:
|
||||||
|
"""
|
||||||
|
raf-fired
|
||||||
|
"""
|
||||||
|
Then the "foo.pdf" PDF should have the following content at page 1:
|
||||||
|
"""
|
||||||
|
ro-fired
|
||||||
|
"""
|
||||||
|
Then the "foo.pdf" PDF should have the following content at page 1:
|
||||||
|
"""
|
||||||
|
io-fired
|
||||||
|
"""
|
||||||
|
|
||||||
Scenario: POST /forms/chromium/convert/html (Wait For Selector)
|
Scenario: POST /forms/chromium/convert/html (Wait For Selector)
|
||||||
Given I have a default Gotenberg container
|
Given I have a default Gotenberg container
|
||||||
|
|||||||
Reference in New Issue
Block a user