diff --git a/Libraries/LibWeb/Layout/LayoutRustBridge.cpp b/Libraries/LibWeb/Layout/LayoutRustBridge.cpp index 141385258c960..bdfd5d5712f72 100644 --- a/Libraries/LibWeb/Layout/LayoutRustBridge.cpp +++ b/Libraries/LibWeb/Layout/LayoutRustBridge.cpp @@ -1660,23 +1660,6 @@ bool can_replay_saved_abspos_layout_inputs_after_style_change(Box const& box) return true; } -static bool can_skip_is_anonymous_text_run(Box& box) -{ - if (box.is_anonymous() && !box.is_generated_for_pseudo_element() && !box.first_child_of_type()) { - bool contains_only_white_space = true; - box.for_each_in_subtree([&](auto const& node) { - if (!is(node) || !static_cast(node).text().is_ascii_whitespace()) { - contains_only_white_space = false; - return TraversalDecision::Break; - } - return TraversalDecision::Continue; - }); - if (contains_only_white_space) - return true; - } - return false; -} - RustFFI::FfiLayoutFcCallbacks LayoutRustBridge::formatting_context_callbacks() { static_assert(to_underlying(Painting::Paintable::ConflictingElementKind::Cell) == 0); @@ -2112,7 +2095,6 @@ RustFFI::FfiLayoutFcCallbacks LayoutRustBridge::formatting_context_callbacks() return; } box.set_default_scroll_shift(static_cast(anchor)->make_weak_ptr(), horizontal, vertical); }, - .can_skip_is_anonymous_text_run = [](void*, void* box) { return can_skip_is_anonymous_text_run(*static_cast(box)); }, }; } diff --git a/Libraries/LibWeb/Layout/TextNode.cpp b/Libraries/LibWeb/Layout/TextNode.cpp index 289848f80acfb..ebca33eba8e77 100644 --- a/Libraries/LibWeb/Layout/TextNode.cpp +++ b/Libraries/LibWeb/Layout/TextNode.cpp @@ -411,6 +411,7 @@ void TextNode::sync_text_content_to_arena() const view.has_ascii_storage() ? reinterpret_cast(view.ascii_span().data()) : nullptr, view.has_ascii_storage() ? nullptr : reinterpret_cast(view.utf16_span().data()), view.length_in_code_units(), + text().is_ascii_whitespace(), Unicode::may_require_bidi_processing(view)); m_arena_text_content_in_sync = true; } diff --git a/Libraries/LibWeb/Rust/src/layout/flex_formatting_context.rs b/Libraries/LibWeb/Rust/src/layout/flex_formatting_context.rs index 687efd1e0373e..4ad7efe2cc0c6 100644 --- a/Libraries/LibWeb/Rust/src/layout/flex_formatting_context.rs +++ b/Libraries/LibWeb/Rust/src/layout/flex_formatting_context.rs @@ -790,10 +790,7 @@ impl<'pass> FlexFormattingContext<'pass> { let next = self.callbacks.next_sibling(child); let facts = self.facts(child); if facts.is_box() { - // SAFETY: The callback only inspects the live layout subtree. - let skip = unsafe { - (self.callbacks.can_skip_is_anonymous_text_run)(self.callbacks.context, self.callbacks.shell(child)) - }; + let skip = self.callbacks.can_skip_is_anonymous_text_run(child); // Skip any "out-of-flow" children if !skip && !facts.is_absolutely_positioned() { // Flex inhibits floating, so only absolute positioning is out of flow here. diff --git a/Libraries/LibWeb/Rust/src/layout/formatting_context.rs b/Libraries/LibWeb/Rust/src/layout/formatting_context.rs index f4afd938eb52f..87b1de30a4183 100644 --- a/Libraries/LibWeb/Rust/src/layout/formatting_context.rs +++ b/Libraries/LibWeb/Rust/src/layout/formatting_context.rs @@ -4891,7 +4891,6 @@ pub struct FfiLayoutFcCallbacks { pub anchor_function_fallback: unsafe extern "C" fn(*mut c_void, *const c_void) -> FfiAnchorFallbackFacts, pub set_resolved_anchor_insets: unsafe extern "C" fn(*mut c_void, *mut c_void, FfiResolvedAnchorInsets), pub set_default_scroll_shift: unsafe extern "C" fn(*mut c_void, *mut c_void, *mut c_void, bool, bool), - pub can_skip_is_anonymous_text_run: unsafe extern "C" fn(*mut c_void, *mut c_void) -> bool, } impl FfiLayoutFcCallbacks { @@ -4917,6 +4916,25 @@ impl FfiLayoutFcCallbacks { unsafe { &*std::ptr::from_ref(content) } } + pub(crate) fn can_skip_is_anonymous_text_run(&self, node: Node) -> bool { + let data = self.node_data(node); + if !crate::layout::has_flag(data, NodeFlag::Anonymous) || data.generated_for != 0 { + return false; + } + + let mut child = data.first_child; + while !child.is_invalid() { + let data = self.node_data(child); + if !crate::layout::kind_is_text(data.kind) + || !self.text_content(child).untransformed_text_is_ascii_whitespace + { + return false; + } + child = data.next_sibling; + } + true + } + pub(crate) fn shell(&self, node: Node) -> *mut c_void { let shell = self.node_data(node).shell; assert!(!shell.is_null()); diff --git a/Libraries/LibWeb/Rust/src/layout/grid_formatting_context.rs b/Libraries/LibWeb/Rust/src/layout/grid_formatting_context.rs index f12013648ef3c..9f2426b8c8cf5 100644 --- a/Libraries/LibWeb/Rust/src/layout/grid_formatting_context.rs +++ b/Libraries/LibWeb/Rust/src/layout/grid_formatting_context.rs @@ -1858,10 +1858,7 @@ impl<'pass> GridFormattingContext<'pass> { let next = self.callbacks.next_sibling(child); let box_facts = self.facts(child); if box_facts.is_box() && !box_facts.is_absolutely_positioned() { - // SAFETY: The callback only inspects this live layout node. - let skip = unsafe { - (self.callbacks.can_skip_is_anonymous_text_run)(self.callbacks.context, self.callbacks.shell(child)) - }; + let skip = self.callbacks.can_skip_is_anonymous_text_run(child); if !skip { self.state.set_box_is_grid_item(&self.callbacks, child, true); let child_grid = self.grid_facts_copy(child); diff --git a/Libraries/LibWeb/Rust/src/layout/layout_node_arena.rs b/Libraries/LibWeb/Rust/src/layout/layout_node_arena.rs index 7b76278efbbc2..39fe430de58e6 100644 --- a/Libraries/LibWeb/Rust/src/layout/layout_node_arena.rs +++ b/Libraries/LibWeb/Rust/src/layout/layout_node_arena.rs @@ -134,6 +134,7 @@ struct SavedAbsposLayoutInputsSlot { #[derive(Default)] pub(crate) struct TextContent { pub(crate) text: Vec, + pub(crate) untransformed_text_is_ascii_whitespace: bool, pub(crate) may_require_bidi_processing: bool, } @@ -643,7 +644,13 @@ impl LayoutNodeArena { } } - pub(crate) fn set_text_content(&mut self, id: NodeSlotId, text: Vec, may_require_bidi_processing: bool) { + pub(crate) fn set_text_content( + &mut self, + id: NodeSlotId, + text: Vec, + untransformed_text_is_ascii_whitespace: bool, + may_require_bidi_processing: bool, + ) { self.assert_owner_thread(); self.data(id); let index = id.slot_index() as usize; @@ -654,6 +661,7 @@ impl LayoutNodeArena { generation: id.generation(), content: Some(Box::new(TextContent { text, + untransformed_text_is_ascii_whitespace, may_require_bidi_processing, })), }; @@ -813,6 +821,7 @@ pub unsafe extern "C" fn layout_arena_set_text_content( ascii_text: *const u8, utf16_text: *const u16, length_in_code_units: usize, + untransformed_text_is_ascii_whitespace: bool, may_require_bidi_processing: bool, ) { abort_on_panic(|| { @@ -834,7 +843,12 @@ pub unsafe extern "C" fn layout_arena_set_text_content( }; // SAFETY: The C++ wrapper keeps the arena alive for this call and // serializes all access on the document thread. - unsafe { &mut *arena.cast::() }.set_text_content(id, text, may_require_bidi_processing); + unsafe { &mut *arena.cast::() }.set_text_content( + id, + text, + untransformed_text_is_ascii_whitespace, + may_require_bidi_processing, + ); }); }