Observation
During repeated web-capture runs against heise.de, three captures began with approximately 120–150 pure-black frames (about 4–5 seconds at 30 fps) before real page content appeared. The behavior occurred with both custom_css and the local custom_js prototype. Five subsequent retries did not reproduce it, so this is intermittent and currently unreproduced on demand.
This bears directly on PR #661, whose first-frame gate is intended to prevent blank/black starts.
Current implementation / hypothesis
The gate treats painted as readiness after either load-complete, the post-paint settle interval, or load_timeout_secs. FrameDelegate::notify_new_frame_ready calls webview.paint() and then sets painted = true, but there is no validation that the rendered surface contains non-black/non-transparent page content. A Servo frame-ready notification may therefore precede meaningful page pixels, allowing the post-paint branch to release early.
A second plausible path is the timeout branch: once load_timeout_secs expires, the node intentionally opens the gate even if no usable paint has arrived; handle_render can then emit the zeroed transparent fallback, which encodes as black, until a later paint produces content. The observed 4–5 second duration is consistent with the web-capture gateway's 5-second timeout cap, but the firing branch was not captured for these runs.
The issue needs instrumentation or a reproducible fixture to distinguish an early-but-empty paint signal from timeout release.
Suggested follow-up
Capture the gate branch and first valid-surface timing for each run, then add a regression fixture that delays real content while exercising the relevant branch. The readiness contract may need a stronger signal than notify_new_frame_ready/painted, or a surface-content check before emission.
Reproduction status
Observation
During repeated web-capture runs against
heise.de, three captures began with approximately 120–150 pure-black frames (about 4–5 seconds at 30 fps) before real page content appeared. The behavior occurred with bothcustom_cssand the localcustom_jsprototype. Five subsequent retries did not reproduce it, so this is intermittent and currently unreproduced on demand.This bears directly on PR #661, whose first-frame gate is intended to prevent blank/black starts.
Current implementation / hypothesis
The gate treats
paintedas readiness after either load-complete, the post-paint settle interval, orload_timeout_secs.FrameDelegate::notify_new_frame_readycallswebview.paint()and then setspainted = true, but there is no validation that the rendered surface contains non-black/non-transparent page content. A Servo frame-ready notification may therefore precede meaningful page pixels, allowing the post-paint branch to release early.A second plausible path is the timeout branch: once
load_timeout_secsexpires, the node intentionally opens the gate even if no usable paint has arrived;handle_rendercan then emit the zeroed transparent fallback, which encodes as black, until a later paint produces content. The observed 4–5 second duration is consistent with the web-capture gateway's 5-second timeout cap, but the firing branch was not captured for these runs.The issue needs instrumentation or a reproducible fixture to distinguish an early-but-empty paint signal from timeout release.
Suggested follow-up
Capture the gate branch and first valid-surface timing for each run, then add a regression fixture that delays real content while exercising the relevant branch. The readiness contract may need a stronger signal than
notify_new_frame_ready/painted, or a surface-content check before emission.Reproduction status
heise.de