From 7002abca3c2b211cb9c138d4957aee8b01e96dfd Mon Sep 17 00:00:00 2001 From: ldm0 Date: Fri, 4 Sep 2026 13:37:49 +0800 Subject: [PATCH] perf(style): share inline source text backing --- moli-dom/src/native/host/mutation/tree.rs | 5 ++++ .../context_host/host_environment.rs | 14 +++++----- .../src/style_engine/source/mod.rs | 25 ++++++++++++----- .../src/style_engine/source/shared_cache.rs | 19 ++++++++++++- .../src/style_engine/source/store.rs | 28 ++++++++++++++++++- .../src/style_engine/source_owner_text.rs | 16 ++++++++--- .../src/style_engine/tests/lifecycle.rs | 6 ++-- 7 files changed, 90 insertions(+), 23 deletions(-) diff --git a/moli-dom/src/native/host/mutation/tree.rs b/moli-dom/src/native/host/mutation/tree.rs index bd360d3faa..9dc0d87e7c 100644 --- a/moli-dom/src/native/host/mutation/tree.rs +++ b/moli-dom/src/native/host/mutation/tree.rs @@ -544,6 +544,11 @@ impl DomHost { self.dom.text_content(handle) } + pub fn shared_text_content(&self, handle: DomHandle) -> Option> { + self.node(handle) + .map(|node| node.shared_text_content(&self.dom)) + } + pub fn inner_html(&self, handle: DomHandle) -> Option { self.dom.inner_html(handle) } diff --git a/moli-renderer-v8/src/native_bridge/context_host/host_environment.rs b/moli-renderer-v8/src/native_bridge/context_host/host_environment.rs index 0c35bde139..bfd62502e9 100644 --- a/moli-renderer-v8/src/native_bridge/context_host/host_environment.rs +++ b/moli-renderer-v8/src/native_bridge/context_host/host_environment.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, sync::Arc}; use super::{ JsContextHost, @@ -873,13 +873,13 @@ impl JsContextHost { } pub(crate) fn sync_owner_style_sheet_text(&mut self, owner: DomHandle) { - let css_text = self.dom_host().text_content(owner).unwrap_or_default(); + let css_text = self + .dom_host() + .shared_text_content(owner) + .unwrap_or_else(|| Arc::from("")); let dom_host = self.dom_host() as *const _; - self.style_engine.sync_owner_style_sheet_text_with_host( - unsafe { &*dom_host }, - owner, - css_text, - ); + self.style_engine + .sync_owner_style_sheet_text_backing_with_host(unsafe { &*dom_host }, owner, css_text); self.install_owner_live_stylesheet(owner); } diff --git a/moli-renderer-v8/src/style_engine/source/mod.rs b/moli-renderer-v8/src/style_engine/source/mod.rs index 77dfad2127..69b10bc50d 100644 --- a/moli-renderer-v8/src/style_engine/source/mod.rs +++ b/moli-renderer-v8/src/style_engine/source/mod.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, sync::Arc}; pub(super) mod adopted; pub(super) mod imports; @@ -160,21 +160,31 @@ impl MoliStyleEngine { .tracks_document(document) } + #[cfg(test)] pub(crate) fn set_owner_style_sheet_text_with_host( &mut self, host: &DomHost, owner: DomHandle, css_text: String, + ) { + self.set_owner_style_sheet_text_backing_with_host(host, owner, css_text.into()); + } + + fn set_owner_style_sheet_text_backing_with_host( + &mut self, + host: &DomHost, + owner: DomHandle, + css_text: Arc, ) { let parser_base = stylesheet_source_base_url(host, owner); self.set_owner_style_sheet_source_with_parser_base(host, owner, css_text, parser_base); } - pub(crate) fn sync_owner_style_sheet_text_with_host( + pub(crate) fn sync_owner_style_sheet_text_backing_with_host( &mut self, host: &DomHost, owner: DomHandle, - css_text: String, + css_text: Arc, ) { if self.owner_document_world(host, owner).is_some_and(|world| { world @@ -184,14 +194,14 @@ impl MoliStyleEngine { }) { return; } - self.set_owner_style_sheet_text_with_host(host, owner, css_text); + self.set_owner_style_sheet_text_backing_with_host(host, owner, css_text); } fn process_owner_style_sheet_text_with_host( &mut self, host: &DomHost, owner: DomHandle, - css_text: String, + css_text: Arc, ) { let Some(document) = owner_document_for_source_owner(host, owner) else { return; @@ -214,7 +224,7 @@ impl MoliStyleEngine { &mut self, host: &DomHost, owner: DomHandle, - css_text: String, + css_text: Arc, parser_base: url::Url, ) { let Some(document) = owner_document_for_source_owner(host, owner) else { @@ -518,7 +528,8 @@ impl MoliStyleEngine { self.process_owner_style_sheet_text_with_host( host, owner, - host.text_content(owner).unwrap_or_default(), + host.shared_text_content(owner) + .unwrap_or_else(|| Arc::from("")), ); } else if matches!( change.kind(), diff --git a/moli-renderer-v8/src/style_engine/source/shared_cache.rs b/moli-renderer-v8/src/style_engine/source/shared_cache.rs index c12f039560..d770e27eeb 100644 --- a/moli-renderer-v8/src/style_engine/source/shared_cache.rs +++ b/moli-renderer-v8/src/style_engine/source/shared_cache.rs @@ -49,13 +49,19 @@ struct SharedStyleSourceCache { pub(super) fn shared_style_source_contents( css_text: String, base_url: url::Url, +) -> Arc { + shared_style_source_contents_from_shared(css_text.into(), base_url) +} + +pub(super) fn shared_style_source_contents_from_shared( + css_text: Arc, + base_url: url::Url, ) -> Arc { let key = SharedStyleSourceCacheKey::new(&css_text, &base_url); if let Some(cached) = CACHE.lock().lookup(&key, &css_text, &base_url) { return cached; } - let css_text = Arc::::from(css_text); let metadata = style_source_metadata_for_css_text(&css_text, &base_url); let source = Arc::new(SharedStyleSourceContents { source_metadata: SharedStyleSourceMetadata::from_metadata( @@ -288,6 +294,17 @@ mod tests { assert_eq!(cache.retained_bytes, 0); } + #[test] + fn shared_input_becomes_the_retained_css_text_backing() { + let css_text = Arc::::from(".shared-input { color: green; }"); + let source = shared_style_source_contents_from_shared( + Arc::clone(&css_text), + url::Url::parse("https://shared-input.test/style.css").expect("valid base URL"), + ); + + assert!(Arc::ptr_eq(&css_text, &source.css_text_handle())); + } + #[test] fn weak_cache_evicts_oldest_index_to_byte_budget() { let mut cache = SharedStyleSourceCache::with_retained_bytes_limit(2 * ENTRY_RETAINED_BYTES); diff --git a/moli-renderer-v8/src/style_engine/source/store.rs b/moli-renderer-v8/src/style_engine/source/store.rs index 52abf400b3..feb2979612 100644 --- a/moli-renderer-v8/src/style_engine/source/store.rs +++ b/moli-renderer-v8/src/style_engine/source/store.rs @@ -13,7 +13,10 @@ use super::super::{ source_key::{StyleSourceKey, StyleSourceSetKey}, }; use super::imports::stylesheet_top_level_import_urls; -use super::shared_cache::{SharedStyleSourceContents, shared_style_source_contents}; +use super::shared_cache::{ + SharedStyleSourceContents, shared_style_source_contents, + shared_style_source_contents_from_shared, +}; use crate::{ document_runtime::DomHandle, protocol_types::EmulatedMediaOverrides, style_engine::StyleViewport, @@ -92,6 +95,15 @@ pub(crate) struct OwnerStyleSheetSource { impl StyloStylesheetSource { pub(crate) fn new(css_text: String, base_url: url::Url) -> Self { let shared = shared_style_source_contents(css_text, base_url); + Self::from_shared_contents(shared) + } + + fn from_shared_text(css_text: StdArc, base_url: url::Url) -> Self { + let shared = shared_style_source_contents_from_shared(css_text, base_url); + Self::from_shared_contents(shared) + } + + fn from_shared_contents(shared: StdArc) -> Self { let cache_key = StyleSourceKey::from_css_fingerprint(shared.css_fingerprint(), shared.base_url()); let base_url = shared.base_url_handle(); @@ -542,8 +554,22 @@ impl StylesheetFontFaceDescriptor { } impl OwnerStyleSheetSource { + #[cfg(test)] pub(crate) fn new(owner: DomHandle, css_text: String, parser_base: url::Url) -> Self { let source = StyloStylesheetSource::new(css_text, parser_base); + Self::from_source(owner, source) + } + + pub(crate) fn from_shared_text( + owner: DomHandle, + css_text: StdArc, + parser_base: url::Url, + ) -> Self { + let source = StyloStylesheetSource::from_shared_text(css_text, parser_base); + Self::from_source(owner, source) + } + + fn from_source(owner: DomHandle, source: StyloStylesheetSource) -> Self { let processing_contents = source .processing_contents() .expect("owner processing source must remain text-backed"); diff --git a/moli-renderer-v8/src/style_engine/source_owner_text.rs b/moli-renderer-v8/src/style_engine/source_owner_text.rs index 7bc5b1a2fd..99fabcebcb 100644 --- a/moli-renderer-v8/src/style_engine/source_owner_text.rs +++ b/moli-renderer-v8/src/style_engine/source_owner_text.rs @@ -94,7 +94,7 @@ impl OwnerStyleSheetSources { pub(super) fn set_source( &mut self, owner: DomHandle, - css_text: String, + css_text: Arc, parser_base: url::Url, ) -> bool { self.cssom_authoritative_owners.remove(&owner); @@ -105,7 +105,11 @@ impl OwnerStyleSheetSources { }) { return false; } - let processing_source = Arc::new(OwnerStyleSheetSource::new(owner, css_text, parser_base)); + let processing_source = Arc::new(OwnerStyleSheetSource::from_shared_text( + owner, + css_text, + parser_base, + )); self.sources_by_owner.insert( owner, InstalledOwnerStyleSheet::from_processing_source(processing_source), @@ -116,11 +120,15 @@ impl OwnerStyleSheetSources { pub(super) fn replace_processed_source( &mut self, owner: DomHandle, - css_text: String, + css_text: Arc, parser_base: url::Url, ) { self.cssom_authoritative_owners.remove(&owner); - let processing_source = Arc::new(OwnerStyleSheetSource::new(owner, css_text, parser_base)); + let processing_source = Arc::new(OwnerStyleSheetSource::from_shared_text( + owner, + css_text, + parser_base, + )); self.sources_by_owner.insert( owner, InstalledOwnerStyleSheet::from_processing_source(processing_source), diff --git a/moli-renderer-v8/src/style_engine/tests/lifecycle.rs b/moli-renderer-v8/src/style_engine/tests/lifecycle.rs index 7f93396dfc..e59589b404 100644 --- a/moli-renderer-v8/src/style_engine/tests/lifecycle.rs +++ b/moli-renderer-v8/src/style_engine/tests/lifecycle.rs @@ -1770,8 +1770,8 @@ fn owner_style_sheet_sources_share_one_cached_text_allocation() { let mut owner_sources = super::source_owner_text::OwnerStyleSheetSources::default(); let base_url = url::Url::parse("https://example.test/").expect("valid test url"); let css_text = ".card:has(.item) { color: red; }"; - owner_sources.set_source(first_owner, css_text.to_owned(), base_url.clone()); - owner_sources.set_source(second_owner, css_text.to_owned(), base_url); + owner_sources.set_source(first_owner, css_text.into(), base_url.clone()); + owner_sources.set_source(second_owner, css_text.into(), base_url); let first = owner_sources .source(first_owner) @@ -1867,7 +1867,7 @@ fn linked_source_store_lifecycle_records_drive_retained_record_construction() { let mut owner_sources = super::source_owner_text::OwnerStyleSheetSources::default(); owner_sources.set_source( style, - ".owner { color: green; }".to_owned(), + ".owner { color: green; }".into(), url::Url::parse("https://example.test/").unwrap(), ); let linked_url = url::Url::parse("https://example.test/linked.css").unwrap();