refactor(renderer): centralize layout invalidation and publication

This commit is contained in:
ldm0
2026-10-04 05:20:17 +08:00
committed by Donough Liu
parent 674a2fd9df
commit 85ceaa4d5c
5 changed files with 60 additions and 27 deletions
@@ -2,6 +2,7 @@ use std::collections::HashSet;
use super::{
JsContextHost,
layout::LayoutInputChange,
permissions::{permission_names_match, permission_override_name},
};
use crate::{
@@ -793,8 +794,7 @@ impl JsContextHost {
overrides: &crate::protocol_types::EmulatedMediaOverrides,
) {
if self.emulated_media != *overrides {
self.style_engine
.bump_target_context_epoch_for_document(self.document_handle());
self.invalidate_layout_inputs(LayoutInputChange::StyleEnvironment);
}
self.emulated_media = overrides.clone();
}
@@ -809,8 +809,7 @@ impl JsContextHost {
) -> bool {
let changed = self.viewport_surface != viewport_surface;
if changed {
self.style_engine
.bump_target_context_epoch_for_document(self.document_handle());
self.invalidate_layout_inputs(LayoutInputChange::StyleEnvironment);
}
self.viewport_surface = viewport_surface;
changed
@@ -14,6 +14,16 @@ use crate::{
style_engine::{StyleViewport, StyloStyleEnvironment},
};
/// Invalidation required by a changed native input. Publication is a separate
/// owner decision; these changes retain the last frozen layout for readers.
#[derive(Clone, Copy)]
pub(crate) enum LayoutInputChange {
/// Media and viewport changes invalidate computed styles through Stylo.
StyleEnvironment,
/// Scrollbar policy and interactions change future geometry/paint output.
UsedGeometry,
}
/// Resets the entry flag on every return path, including unwinding.
///
/// This is a synchronous ownership guard, not a generation or a retry fence.
@@ -57,13 +67,26 @@ impl JsContextHost {
self.layout_policy
}
pub(crate) fn set_scrollbars_hidden(&mut self, hidden: bool) {
if self.scrollbars_hidden != hidden {
pub(crate) fn set_scrollbars_hidden(&mut self, hidden: bool) -> bool {
let changed = self.scrollbars_hidden != hidden;
if changed {
self.scrollbars_hidden = hidden;
self.clear_layout_rect_cache();
self.document_layout_state
.get_mut()
.mark_visual_state_dirty();
self.invalidate_layout_inputs(LayoutInputChange::UsedGeometry);
}
changed
}
pub(crate) fn invalidate_layout_inputs(&self, change: LayoutInputChange) {
match change {
LayoutInputChange::StyleEnvironment => self
.style_engine
.bump_target_context_epoch_for_document(self.document_handle()),
LayoutInputChange::UsedGeometry => {
self.clear_layout_rect_cache();
self.document_layout_state
.borrow_mut()
.mark_visual_state_dirty();
}
}
}
@@ -519,12 +542,7 @@ impl JsContextHost {
}
pub(crate) fn invalidate_layout_after_interaction_state_change(&self) {
self.clear_layout_rect_cache();
// Interactions dirty future visual publication, while ordinary geometry
// and input keep consuming the last published frozen snapshot.
self.document_layout_state
.borrow_mut()
.mark_visual_state_dirty();
self.invalidate_layout_inputs(LayoutInputChange::UsedGeometry);
}
pub(crate) fn document_web_font_resources_are_current(
@@ -4,7 +4,7 @@ use crate::script_vm::{DocumentSetting, DocumentSettingEffect, DocumentSettingsA
impl PageVm {
fn apply_document_setting(&mut self, setting: DocumentSetting<'_>) -> anyhow::Result<()> {
let layout = self.layout_configuration;
let effect = self.vm_mut().apply_document_setting(
let effect: DocumentSettingEffect = self.vm_mut().apply_document_setting(
setting,
layout,
DocumentSettingsApplication::Live,
@@ -12,10 +12,8 @@ impl PageVm {
// Retain the target value before publication. Even if publication
// fails, a followed navigation must inherit the accepted native input.
setting.retain_in(&mut self.document_settings);
if effect == DocumentSettingEffect::PublishLayout {
self.vm_mut().publish_layout()?;
}
Ok(())
self.vm_mut()
.complete_document_settings_application(DocumentSettingsApplication::Live, effect)
}
pub(crate) fn set_script_execution_disabled(&mut self, disabled: bool) -> anyhow::Result<()> {
@@ -198,10 +198,10 @@ impl ScriptVm {
self._context_host.borrow_mut().set_layout_policy(policy);
}
pub(crate) fn set_scrollbars_hidden(&mut self, hidden: bool) {
pub(crate) fn set_scrollbars_hidden(&mut self, hidden: bool) -> bool {
self._context_host
.borrow_mut()
.set_scrollbars_hidden(hidden);
.set_scrollbars_hidden(hidden)
}
pub(super) fn sync_document_fonts_for_environment(&mut self) {
@@ -12,7 +12,9 @@ pub(crate) enum DocumentSettingsApplication {
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum DocumentSettingEffect {
/// Native values are installed; queued rendering updates publish later.
Deferred,
/// Geometry must be current before a live setting command is acknowledged.
PublishLayout,
}
@@ -75,7 +77,9 @@ impl ScriptVm {
DocumentSetting::DocumentActivity(*document_activity),
DocumentSetting::ScrollbarsHidden(*scrollbars_hidden),
] {
self.apply_document_setting(setting, layout, DocumentSettingsApplication::Bootstrap)?;
let application = DocumentSettingsApplication::Bootstrap;
let effect = self.apply_document_setting(setting, layout, application)?;
self.complete_document_settings_application(application, effect)?;
}
Ok(())
}
@@ -134,13 +138,27 @@ impl ScriptVm {
}
DocumentSetting::ScrollbarsHidden(value) => {
let hidden = layout.scrollbars_hidden_for(value);
let changed = self._context_host.borrow().scrollbars_hidden() != hidden;
self.set_scrollbars_hidden(hidden);
if live && changed {
if self.set_scrollbars_hidden(hidden) {
effect = DocumentSettingEffect::PublishLayout;
}
}
}
Ok(effect)
}
/// Initialization never publishes layout or runs rendering notifications.
/// Live commands publish only settings that require synchronous geometry.
pub(crate) fn complete_document_settings_application(
&mut self,
application: DocumentSettingsApplication,
effect: DocumentSettingEffect,
) -> anyhow::Result<()> {
match (application, effect) {
(DocumentSettingsApplication::Bootstrap, _)
| (DocumentSettingsApplication::Live, DocumentSettingEffect::Deferred) => Ok(()),
(DocumentSettingsApplication::Live, DocumentSettingEffect::PublishLayout) => {
self.publish_layout()
}
}
}
}