diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 8ed3f9d33dc..f4330ccecb7 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -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, diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index fa68fcfe926..036bd601c03 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -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 }