Move out idle message sending

This commit is contained in:
Martin Robinson 2025-07-14 13:40:30 +02:00
parent 3640c027f2
commit 851195c3ce
2 changed files with 66 additions and 49 deletions

View file

@ -2280,22 +2280,18 @@ impl Window {
/// only exceptions are script queries and scroll requests.
pub(crate) fn reflow(&self, reflow_goal: ReflowGoal, can_gc: CanGc) -> bool {
// Never reflow inactive Documents.
if !self.Document().is_fully_active() {
let document = self.Document();
if !document.is_fully_active() {
return false;
}
// Count the pending web fonts before layout, in case a font loads during the layout.
let waiting_for_web_fonts_to_load = self.font_context.web_fonts_still_loading() != 0;
self.Document().ensure_safe_to_run_script_or_layout();
document.ensure_safe_to_run_script_or_layout();
let updating_the_rendering = reflow_goal == ReflowGoal::UpdateTheRendering;
let issued_reflow = self.force_reflow(reflow_goal);
let document = self.Document();
let font_face_set = document.Fonts(can_gc);
let is_ready_state_complete = document.ReadyState() == DocumentReadyState::Complete;
// From https://drafts.csswg.org/css-font-loading/#font-face-set-ready:
// > A FontFaceSet is pending on the environment if any of the following are true:
// > - the document is still loading
@ -2306,49 +2302,60 @@ impl Window {
// Thus, we are queueing promise resolution here. This reflow should have been triggered by
// a "rendering opportunity" in `ScriptThread::handle_web_font_loaded, which should also
// make sure a microtask checkpoint happens, triggering the promise callback.
let font_face_set = document.Fonts(can_gc);
let is_ready_state_complete = document.ReadyState() == DocumentReadyState::Complete;
if !waiting_for_web_fonts_to_load && is_ready_state_complete {
font_face_set.fulfill_ready_promise_if_needed(can_gc);
}
// If writing a screenshot, check if the script has reached a state
// where it's safe to write the image. This means that:
// 1) The reflow is for display (otherwise it could be a query)
// 2) The html element doesn't contain the 'reftest-wait' class
// 3) The load event has fired.
issued_reflow
}
pub(crate) fn maybe_send_idle_document_state_to_constellation(&self) {
if !opts::get().wait_for_stable_image {
return;
}
if self.has_sent_idle_message.get() {
return;
}
let document = self.Document();
if document.ReadyState() != DocumentReadyState::Complete {
return;
}
// Checks if the html element has reftest-wait attribute present.
// See http://testthewebforward.org/docs/reftests.html
// and https://web-platform-tests.org/writing-tests/crashtest.html
if document.GetDocumentElement().is_some_and(|elem| {
elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
}) {
return;
}
if self.font_context.web_fonts_still_loading() != 0 {
return;
}
if !self.pending_layout_images.borrow().is_empty() ||
!self.pending_images_for_rasterization.borrow().is_empty()
{
return;
}
// When all these conditions are met, notify the constellation
// that this pipeline is ready to write the image (from the script thread
// perspective at least).
if opts::get().wait_for_stable_image && updating_the_rendering {
// Checks if the html element has reftest-wait attribute present.
// See http://testthewebforward.org/docs/reftests.html
// and https://web-platform-tests.org/writing-tests/crashtest.html
let html_element = document.GetDocumentElement();
let reftest_wait = html_element.is_some_and(|elem| {
elem.has_class(&atom!("reftest-wait"), CaseSensitivity::CaseSensitive) ||
elem.has_class(&Atom::from("test-wait"), CaseSensitivity::CaseSensitive)
});
let has_sent_idle_message = self.has_sent_idle_message.get();
let no_pending_images = self.pending_layout_images.borrow().is_empty() &&
self.pending_images_for_rasterization.borrow().is_empty();
if !has_sent_idle_message &&
is_ready_state_complete &&
!reftest_wait &&
no_pending_images &&
!waiting_for_web_fonts_to_load
{
debug!(
"{:?}: Sending DocumentState::Idle to Constellation",
self.pipeline_id()
);
let event = ScriptToConstellationMessage::SetDocumentState(DocumentState::Idle);
self.send_to_constellation(event);
self.has_sent_idle_message.set(true);
}
}
issued_reflow
debug!(
"{:?}: Sending DocumentState::Idle to Constellation",
self.pipeline_id()
);
self.send_to_constellation(ScriptToConstellationMessage::SetDocumentState(
DocumentState::Idle,
));
self.has_sent_idle_message.set(true);
}
/// If parsing has taken a long time and reflows are still waiting for the `load` event,

View file

@ -1369,15 +1369,12 @@ impl ScriptThread {
// should be run in a task and a microtask checkpoint is always done when running tasks.
self.perform_a_microtask_checkpoint(can_gc);
self.maybe_schedule_rendering_opportunity_after_rendering_upate(saw_any_reflows);
self.maybe_schedule_rendering_opportunity_after_rendering_update(saw_any_reflows);
}
fn maybe_schedule_rendering_opportunity_after_rendering_upate(&self, saw_any_reflows: bool) {
fn maybe_schedule_rendering_opportunity_after_rendering_update(&self, saw_any_reflows: bool) {
// If there are any pending reflows and we are not having rendering opportunities
// driven by the compositor, then schedule the next rendering opportunity.
//
// TODO: This is a workaround until rendering opportunities can be triggered from a
// timer in the script thread.
// immediately after running "update the rendering," run it one more time.
if self
.documents
.borrow()
@ -1403,6 +1400,18 @@ impl ScriptThread {
}
}
fn maybe_send_idle_document_state_to_constellation(&self) {
if !opts::get().wait_for_stable_image {
return;
}
for (_, document) in self.documents.borrow().iter() {
document
.window()
.maybe_send_idle_document_state_to_constellation();
}
}
fn maybe_schedule_rendering_opportunity_after_ipc_message(&self, can_gc: CanGc) {
if self.has_pending_animation_tick.load(Ordering::Relaxed) {
self.update_the_rendering(can_gc);
@ -1650,6 +1659,7 @@ impl ScriptThread {
}
self.maybe_schedule_rendering_opportunity_after_ipc_message(can_gc);
self.maybe_send_idle_document_state_to_constellation();
true
}