perf(layout): remove per-style anonymous provenance

This commit is contained in:
ldm0
2026-10-16 21:35:36 +08:00
committed by Donough Liu
parent deb111966a
commit 0059a9b238
8 changed files with 133 additions and 173 deletions
+32 -20
View File
@@ -1,33 +1,45 @@
use crate::ResolvedLayoutStyle;
/// Renderer-assigned provenance of a projected style. Pass identities must
/// remain unique while any of their styles can still be used as parents.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LayoutStyleOrigin {
pass: u64,
owner: u64,
}
impl LayoutStyleOrigin {
pub const fn new(pass: u64, owner: u64) -> Self {
Self { pass, owner }
}
}
use crate::{LayoutBox, ResolvedLayoutStyle};
/// Inheritance input for an anonymous box in the resolver's prepared pass.
/// The owner supplies provenance and diagnostics; it does not select a style
/// world or request another element-style observation.
/// Owner and style are borrowed from the same parent box. During construction,
/// layout may also pair a newly resolved style with its source before its box
/// exists. Renderers consume this input without selecting another style world
/// or observing the owner's element style again.
#[derive(Clone, Copy, Debug)]
pub struct LayoutAnonymousStyleContext<'a, N> {
pub owner: N,
pub parent_style: &'a ResolvedLayoutStyle,
owner: N,
parent_style: &'a ResolvedLayoutStyle,
}
impl<'a, N> LayoutAnonymousStyleContext<'a, N> {
pub const fn new(owner: N, parent_style: &'a ResolvedLayoutStyle) -> Self {
pub(crate) const fn new(owner: N, parent_style: &'a ResolvedLayoutStyle) -> Self {
Self {
owner,
parent_style,
}
}
/// Derives both inputs from one parent instead of accepting an unrelated
/// owner and style. A parent without a source or anonymous owner has no
/// native inheritance context.
pub fn from_parent(parent: &'a LayoutBox<N>) -> Option<Self>
where
N: Copy,
{
Some(Self::new(
parent.source().or(parent.owner())?,
parent.style(),
))
}
pub const fn owner(&self) -> N
where
N: Copy,
{
self.owner
}
pub const fn parent_style(&self) -> &'a ResolvedLayoutStyle {
self.parent_style
}
}
+2 -1
View File
@@ -53,7 +53,8 @@ where
None
};
let editor_style = styles.anonymous_style(
LayoutAnonymousStyleContext::new(owner, &world.boxes[control.index()].style),
LayoutAnonymousStyleContext::from_parent(&world.boxes[control.index()])
.expect("form control has a source owner"),
LayoutDisplay::Block,
)?;
let selection = world.boxes[control.index()].text_selection;
+1 -1
View File
@@ -41,7 +41,7 @@ mod taffy_tree;
mod text;
mod world;
pub use anonymous_style::{LayoutAnonymousStyleContext, LayoutStyleOrigin};
pub use anonymous_style::LayoutAnonymousStyleContext;
pub use builder::build_layout_world;
pub use capture::{
FULL_DOCUMENT_CAPTURE_CSS_DIMENSION_LIMIT, PaintCaptureRegion, PaintCaptureRequest,
+1 -1
View File
@@ -666,7 +666,7 @@ where
display: crate::LayoutDisplay,
) -> Result<ResolvedLayoutStyle, LayoutError> {
Ok(ResolvedLayoutStyle::anonymous_from(
context.parent_style,
context.parent_style(),
display,
))
}
-16
View File
@@ -482,7 +482,6 @@ impl ResolvedLayoutTransform {
/// invalid.
#[derive(Clone)]
pub struct ResolvedLayoutStyle {
source_origin: Option<crate::LayoutStyleOrigin>,
pub(crate) computed: Option<ServoArc<ComputedValues>>,
pub(crate) taffy: Style<Atom>,
preferred_aspect_ratio: PreferredAspectRatio,
@@ -866,7 +865,6 @@ impl ResolvedLayoutStyle {
};
}
Self {
source_origin: None,
computed: Some(computed),
taffy,
preferred_aspect_ratio,
@@ -944,7 +942,6 @@ impl ResolvedLayoutStyle {
};
let sticky_inset = taffy.inset;
Self {
source_origin: None,
computed: None,
taffy,
preferred_aspect_ratio,
@@ -1170,17 +1167,6 @@ impl ResolvedLayoutStyle {
self.computed.as_ref()
}
/// Binds a renderer projection to its pass and source. Cloning and
/// deriving anonymous/text styles preserve this identity.
pub fn with_source_origin(mut self, origin: crate::LayoutStyleOrigin) -> Self {
self.source_origin = Some(origin);
self
}
pub const fn source_origin(&self) -> Option<crate::LayoutStyleOrigin> {
self.source_origin
}
pub fn background_color(&self) -> PaintColor {
self.background_color
}
@@ -1993,7 +1979,6 @@ impl ResolvedLayoutStyle {
pub(crate) fn text_leaf_from(parent: &Self) -> Self {
Self {
source_origin: parent.source_origin,
computed: parent.computed.clone(),
taffy: Style {
display: TaffyDisplay::Block,
@@ -2061,7 +2046,6 @@ impl ResolvedLayoutStyle {
/// Stylo allocation (primarily DOM-free tests and conservative fallback).
pub fn anonymous_from(parent: &Self, display: LayoutDisplay) -> Self {
Self {
source_origin: parent.source_origin,
computed: parent.computed.clone(),
taffy: Style {
display: taffy_display(display),
+2 -2
View File
@@ -196,9 +196,9 @@ impl LayoutStyleResolver<usize> for TestStyles {
context: moli_layout::LayoutAnonymousStyleContext<'_, usize>,
display: LayoutDisplay,
) -> Result<ResolvedLayoutStyle, LayoutError> {
self.anonymous_queries.push((context.owner, display));
self.anonymous_queries.push((context.owner(), display));
Ok(ResolvedLayoutStyle::anonymous_from(
context.parent_style,
context.parent_style(),
display,
))
}
+1 -1
View File
@@ -145,7 +145,7 @@ impl LayoutStyleResolver<usize> for FixtureStyles {
display: LayoutDisplay,
) -> Result<ResolvedLayoutStyle, LayoutError> {
Ok(ResolvedLayoutStyle::anonymous_from(
context.parent_style,
context.parent_style(),
display,
))
}
@@ -1,12 +1,8 @@
use moli_layout::{
LayoutAnonymousStyleContext, LayoutDisplay, LayoutError, LayoutStyleOrigin,
LayoutStyleResolver, LayoutViewport, ResolvedLayoutElementStyles, ResolvedLayoutPseudoStyle,
ResolvedLayoutStyle,
};
use std::{
sync::atomic::{AtomicU64, Ordering},
time::Instant,
LayoutAnonymousStyleContext, LayoutDisplay, LayoutError, LayoutStyleResolver, LayoutViewport,
ResolvedLayoutElementStyles, ResolvedLayoutPseudoStyle, ResolvedLayoutStyle,
};
use std::time::Instant;
use crate::{
document_runtime::DomHandle,
@@ -17,13 +13,10 @@ use crate::{
style_engine::{StyleViewport, StyloAnonymousBoxKind},
};
static NEXT_STYLE_PASS: AtomicU64 = AtomicU64::new(1);
pub(super) struct NativeLayoutStyleResolver<'a> {
runtime: &'a JsContextHost,
reads: StyleObservation<'a>,
anonymous_context: Option<PreparedAnonymousStyleContext<'a>>,
pass_id: u64,
scripting_enabled: bool,
profile: Option<NativeLayoutStyleResolverProfile>,
}
@@ -59,21 +52,12 @@ impl<'a> NativeLayoutStyleResolver<'a> {
runtime,
reads,
anonymous_context,
pass_id: NEXT_STYLE_PASS
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |next| {
next.checked_add(1)
})
.expect("layout style pass identity exhausted"),
scripting_enabled: runtime.document_scripting_enabled(document),
profile: moli_trace::cpu_profile_enabled()
.then(NativeLayoutStyleResolverProfile::default),
}
}
fn style_origin(&self, owner: DomHandle) -> LayoutStyleOrigin {
LayoutStyleOrigin::new(self.pass_id, u64::from(owner.index_u32()))
}
pub(super) fn trace_profile(&self, document: crate::document_runtime::DomHandle) {
let Some(profile) = self.profile.as_ref() else {
return;
@@ -152,8 +136,7 @@ impl LayoutStyleResolver<DomHandle> for NativeLayoutStyleResolver<'_> {
);
}
let phase_started = self.profile.as_ref().map(|_| Instant::now());
let origin = self.style_origin(node);
let mut resolved = ResolvedLayoutStyle::from_stylo(computed).with_source_origin(origin);
let mut resolved = ResolvedLayoutStyle::from_stylo(computed);
if let Some(profile) = self.profile.as_mut() {
profile.primary_projection_ns = profile.primary_projection_ns.saturating_add(
phase_started
@@ -162,10 +145,8 @@ impl LayoutStyleResolver<DomHandle> for NativeLayoutStyleResolver<'_> {
);
}
let phase_started = self.profile.as_ref().map(|_| Instant::now());
let before = before
.map(|computed| ResolvedLayoutStyle::from_stylo(computed).with_source_origin(origin));
let after = after
.map(|computed| ResolvedLayoutStyle::from_stylo(computed).with_source_origin(origin));
let before = before.map(ResolvedLayoutStyle::from_stylo);
let after = after.map(ResolvedLayoutStyle::from_stylo);
if let Some(profile) = self.profile.as_mut() {
profile.eager_pseudo_count = profile
.eager_pseudo_count
@@ -217,11 +198,8 @@ impl LayoutStyleResolver<DomHandle> for NativeLayoutStyleResolver<'_> {
);
}
let phase_started = self.profile.as_ref().map(|_| Instant::now());
let origin = self.style_origin(node);
let resolved = computed.map(|computed| {
ResolvedLayoutPseudoStyle::new(
ResolvedLayoutStyle::from_stylo(computed).with_source_origin(origin),
)
ResolvedLayoutPseudoStyle::new(ResolvedLayoutStyle::from_stylo(computed))
});
if let Some(profile) = self.profile.as_mut() {
profile.marker_projection_ns = profile.marker_projection_ns.saturating_add(
@@ -237,15 +215,12 @@ impl LayoutStyleResolver<DomHandle> for NativeLayoutStyleResolver<'_> {
&mut self,
node: DomHandle,
) -> Result<Option<ResolvedLayoutPseudoStyle>, LayoutError> {
let origin = self.style_origin(node);
Ok(self
.reads
.read(node)
.pseudo_computed_values("placeholder")
.map(|computed| {
ResolvedLayoutPseudoStyle::new(
ResolvedLayoutStyle::from_stylo(computed).with_source_origin(origin),
)
ResolvedLayoutPseudoStyle::new(ResolvedLayoutStyle::from_stylo(computed))
}))
}
@@ -254,8 +229,8 @@ impl LayoutStyleResolver<DomHandle> for NativeLayoutStyleResolver<'_> {
context: LayoutAnonymousStyleContext<'_, DomHandle>,
display: LayoutDisplay,
) -> Result<ResolvedLayoutStyle, LayoutError> {
let owner = context.owner;
let parent = context.parent_style;
let owner = context.owner();
let parent = context.parent_style();
let parent_computed = parent.stylo_computed_values().ok_or_else(|| {
LayoutError::style_resolution(
format!("{owner:?}"),
@@ -276,13 +251,6 @@ impl LayoutStyleResolver<DomHandle> for NativeLayoutStyleResolver<'_> {
"anonymous owner document does not match the prepared style context",
));
}
let origin = self.style_origin(owner);
if parent.source_origin() != Some(origin) {
return Err(LayoutError::style_resolution(
format!("{owner:?}"),
"anonymous parent style does not match its owner and layout pass",
));
}
let computed = prepared
.computed_values(owner, parent_computed.as_ref(), anonymous_kind)
.ok_or_else(|| {
@@ -300,7 +268,7 @@ impl LayoutStyleResolver<DomHandle> for NativeLayoutStyleResolver<'_> {
);
}
let phase_started = self.profile.as_ref().map(|_| Instant::now());
let mut resolved = ResolvedLayoutStyle::from_stylo(computed).with_source_origin(origin);
let mut resolved = ResolvedLayoutStyle::from_stylo(computed);
resolved.force_layout_display(display);
if let Some(profile) = self.profile.as_mut() {
profile.anonymous_projection_ns = profile.anonymous_projection_ns.saturating_add(
@@ -368,12 +336,20 @@ mod tests {
vm
}
fn parent_style(
fn layout_world(
resolver: &mut NativeLayoutStyleResolver<'_>,
root: DomHandle,
) -> moli_layout::LayoutWorld<DomHandle> {
let source = super::super::source_view::NativeLayoutSourceView::new(resolver.runtime, root);
moli_layout::build_layout_world(&source, resolver).unwrap()
}
fn parent_context(
world: &moli_layout::LayoutWorld<DomHandle>,
owner: DomHandle,
) -> ResolvedLayoutStyle {
ResolvedLayoutStyle::from_stylo(resolver.reads.read(owner).computed_values().unwrap())
.with_source_origin(resolver.style_origin(owner))
) -> LayoutAnonymousStyleContext<'_, DomHandle> {
let parent = world.box_by_id(world.source_box(owner).unwrap()).unwrap();
LayoutAnonymousStyleContext::from_parent(parent).unwrap()
}
#[test]
@@ -386,16 +362,14 @@ mod tests {
let owner = host.dom_host().element_handle_by_id("owner").unwrap();
let viewport = host.layout_viewport_for_document(document);
let mut resolver = NativeLayoutStyleResolver::new(&host, root, document, viewport);
let parent = parent_style(&mut resolver, owner);
let world = layout_world(&mut resolver, root);
let parent = parent_context(&world, owner);
let reads = resolver.reads.read_count_for_test();
for _ in 0..3 {
resolver.profile = Some(NativeLayoutStyleResolverProfile::default());
for _ in 0..10_000 {
let style = resolver
.anonymous_style(
LayoutAnonymousStyleContext::new(owner, &parent),
LayoutDisplay::Block,
)
.anonymous_style(parent, LayoutDisplay::Block)
.unwrap();
assert_eq!(style.display(), LayoutDisplay::Block);
std::hint::black_box(style);
@@ -415,7 +389,7 @@ mod tests {
}
#[test]
fn anonymous_styles_reject_mismatched_owners_documents_and_passes() {
fn anonymous_styles_reject_foreign_documents() {
let mut vm = fixture();
vm.eval("document.getElementById('frame').contentDocument.body.textContent = 'child'")
.unwrap();
@@ -424,38 +398,10 @@ mod tests {
let document = host.document_handle();
let root = host.dom_host().document_element_handle().unwrap();
let owner = host.dom_host().element_handle_by_id("owner").unwrap();
let other = host.dom_host().element_handle_by_id("other").unwrap();
let viewport = host.layout_viewport_for_document(document);
let mut resolver = NativeLayoutStyleResolver::new(&host, root, document, viewport);
let parent = parent_style(&mut resolver, owner);
let other_parent = parent_style(&mut resolver, other);
for (owner, parent) in [(owner, &other_parent), (other, &parent)] {
let error = resolver
.anonymous_style(
LayoutAnonymousStyleContext::new(owner, parent),
LayoutDisplay::Block,
)
.unwrap_err();
assert!(
error.to_string().contains("owner and layout pass"),
"{error}"
);
}
let mut inherited = parent.clone();
let reads = resolver.reads.read_count_for_test();
for display in [
LayoutDisplay::Table,
LayoutDisplay::TableRow,
LayoutDisplay::TableCell,
] {
inherited = resolver
.anonymous_style(LayoutAnonymousStyleContext::new(owner, &inherited), display)
.unwrap();
assert_eq!(inherited.font_size(), parent.font_size());
assert_eq!(inherited.source_origin(), parent.source_origin());
}
assert_eq!(resolver.reads.read_count_for_test(), reads);
let world = layout_world(&mut resolver, root);
let parent = parent_context(&world, owner);
let frame = host.dom_host().element_handle_by_id("frame").unwrap();
let foreign_document = host.child_browsing_context_document_handle(frame).unwrap();
@@ -466,30 +412,17 @@ mod tests {
.unwrap();
let mut foreign =
NativeLayoutStyleResolver::new(&host, foreign_root, foreign_document, viewport);
let foreign_parent = parent_style(&mut foreign, foreign_root);
let foreign_world = layout_world(&mut foreign, foreign_root);
let foreign_parent = parent_context(&foreign_world, foreign_root);
assert!(
foreign
.anonymous_style(
LayoutAnonymousStyleContext::new(foreign_root, &foreign_parent),
LayoutDisplay::Block
)
.anonymous_style(foreign_parent, LayoutDisplay::Block)
.is_ok()
);
let error = resolver
.anonymous_style(
LayoutAnonymousStyleContext::new(foreign_root, &foreign_parent),
LayoutDisplay::Block,
)
.anonymous_style(foreign_parent, LayoutDisplay::Block)
.unwrap_err();
assert!(error.to_string().contains("owner document"), "{error}");
assert!(
resolver
.anonymous_style(
LayoutAnonymousStyleContext::new(owner, &foreign_parent),
LayoutDisplay::Block
)
.is_err()
);
assert!(
resolver
.anonymous_context
@@ -497,27 +430,70 @@ mod tests {
.unwrap()
.computed_values(
foreign_root,
parent.stylo_computed_values().unwrap(),
parent.parent_style().stylo_computed_values().unwrap(),
StyloAnonymousBoxKind::Generic
)
.is_none()
);
}
let mut next = NativeLayoutStyleResolver::new(&host, root, document, viewport);
assert!(
next.anonymous_style(
LayoutAnonymousStyleContext::new(owner, &parent),
LayoutDisplay::Block
)
.is_err()
#[test]
fn anonymous_table_styles_inherit_from_their_parent_boxes() {
let mut vm = fixture();
vm.eval(
r#"document.getElementById('owner').innerHTML = '<span style="display:table-cell">one</span><span style="display:table-row">two</span>'"#,
)
.unwrap();
vm.sync_live_document_style_sources();
let host = vm.context_host_weak_for_test().upgrade().unwrap();
let host = host.borrow();
let document = host.document_handle();
let root = host.dom_host().document_element_handle().unwrap();
let owner = host.dom_host().element_handle_by_id("owner").unwrap();
let mut resolver = NativeLayoutStyleResolver::new(
&host,
root,
document,
host.layout_viewport_for_document(document),
);
let world = layout_world(&mut resolver, root);
let reads = resolver.reads.read_count_for_test();
let mut pending = vec![world.source_box(owner).unwrap()];
let mut displays = Vec::new();
while let Some(id) = pending.pop() {
let parent = world.box_by_id(id).unwrap();
pending.extend_from_slice(parent.children());
if parent.anonymous_reason().is_none() {
continue;
}
let context = LayoutAnonymousStyleContext::from_parent(parent).unwrap();
assert_eq!(context.owner(), parent.owner().unwrap());
assert_eq!(context.parent_style().font_size(), 23.0);
let anonymous = resolver
.anonymous_style(context, LayoutDisplay::Block)
.unwrap();
assert_eq!(anonymous.font_size(), 23.0);
displays.push(parent.style().display());
}
for display in [
LayoutDisplay::Table,
LayoutDisplay::TableRowGroup,
LayoutDisplay::TableRow,
LayoutDisplay::TableCell,
] {
assert!(
displays.contains(&display),
"missing {display:?}: {displays:?}"
);
}
assert_eq!(resolver.reads.read_count_for_test(), reads);
}
#[test]
fn anonymous_styles_prepare_pending_invalidations_for_each_pass() {
let mut vm = fixture();
vm.sync_live_document_style_sources();
let initial = {
{
let host = vm.context_host_weak_for_test().upgrade().unwrap();
let host = host.borrow();
let document = host.document_handle();
@@ -529,20 +505,17 @@ mod tests {
document,
host.layout_viewport_for_document(document),
);
let parent = parent_style(&mut resolver, owner);
assert_eq!(parent.font_size(), 23.0);
let world = layout_world(&mut resolver, root);
let parent = parent_context(&world, owner);
assert_eq!(parent.parent_style().font_size(), 23.0);
assert_eq!(
resolver
.anonymous_style(
LayoutAnonymousStyleContext::new(owner, &parent),
LayoutDisplay::Block
)
.anonymous_style(parent, LayoutDisplay::Block)
.unwrap()
.font_size(),
23.0
);
parent
};
}
for (mutation, expected) in [
(
"document.getElementById('owner').style.fontSize = '37px'",
@@ -566,25 +539,15 @@ mod tests {
document,
host.layout_viewport_for_document(document),
);
let parent = parent_style(&mut resolver, owner);
assert_eq!(parent.font_size(), expected);
let world = layout_world(&mut resolver, root);
let parent = parent_context(&world, owner);
assert_eq!(parent.parent_style().font_size(), expected);
let reads = resolver.reads.read_count_for_test();
let anonymous = resolver
.anonymous_style(
LayoutAnonymousStyleContext::new(owner, &parent),
LayoutDisplay::Block,
)
.anonymous_style(parent, LayoutDisplay::Block)
.unwrap();
assert_eq!(anonymous.font_size(), expected);
assert_eq!(resolver.reads.read_count_for_test(), reads);
assert!(
resolver
.anonymous_style(
LayoutAnonymousStyleContext::new(owner, &initial),
LayoutDisplay::Block
)
.is_err()
);
}
}