layout: Use ServoThreadSafeLayoutNode in more places (#38626)

Use `ServoThreadSafeLayoutNode` in more places in layout rather than
`ServoLayoutNode`. The former is meant to be used during layout, but
layout 2020 was written against the latter. In general, this reduces the
amount of conversion to the thread-safe version in many places in
layout.

In addition, an unused iterator from the `script` crate
`ServoThreadSafeLayoutNodeChildrenIterator` is replaced with the child
iterator from `layout`. The `layout` version must be directly in
`script` now as it uses the dangerous variants of `next_sibling` and
`first_child`, which allow encapsulating the unsafe bits into one
module.

This will ultimately be useful for storing the layout data of
pseudo-element children of pseudo-elements properly.

Testing: This should not change any behavior and thus is covered by
existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2025-08-13 16:55:19 +02:00 committed by GitHub
parent 20ad1ce84e
commit ee7c1d9109
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 258 additions and 321 deletions

View file

@ -2,22 +2,19 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::any::Any;
use std::marker::PhantomData;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use base::id::{BrowsingContextId, PipelineId};
use html5ever::{local_name, ns};
use layout_api::wrapper_traits::{
LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
};
use layout_api::wrapper_traits::{LayoutDataTrait, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use layout_api::{
GenericLayoutDataTrait, LayoutDamage, LayoutElementType,
LayoutNodeType as ScriptLayoutNodeType, SVGElementData,
};
use malloc_size_of_derive::MallocSizeOf;
use net_traits::image_cache::Image;
use script::layout_dom::ServoLayoutNode;
use script::layout_dom::ServoThreadSafeLayoutNode;
use servo_arc::Arc as ServoArc;
use smallvec::SmallVec;
use style::context::SharedStyleContext;
@ -116,13 +113,13 @@ impl LayoutBox {
fn repair_style(
&self,
context: &SharedStyleContext,
node: &ServoLayoutNode,
node: &ServoThreadSafeLayoutNode,
new_style: &ServoArc<ComputedValues>,
) {
match self {
LayoutBox::DisplayContents(inline_shared_styles) => {
*inline_shared_styles.style.borrow_mut() = new_style.clone();
*inline_shared_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
*inline_shared_styles.selected.borrow_mut() = node.selected_style();
},
LayoutBox::BlockLevel(block_level_box) => {
block_level_box
@ -172,7 +169,7 @@ pub struct DOMLayoutData(AtomicRefCell<InnerDOMLayoutData>);
// The implementation of this trait allows the data to be stored in the DOM.
impl LayoutDataTrait for DOMLayoutData {}
impl GenericLayoutDataTrait for DOMLayoutData {
fn as_any(&self) -> &dyn Any {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
@ -235,7 +232,6 @@ pub(crate) trait NodeExt<'dom> {
fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>;
fn as_svg(&self) -> Option<SVGElementData>;
fn as_typeless_object_with_data_attribute(&self) -> Option<String>;
fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues>;
fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
@ -255,10 +251,9 @@ pub(crate) trait NodeExt<'dom> {
fn take_restyle_damage(&self) -> LayoutDamage;
}
impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
impl<'dom> NodeExt<'dom> for ServoThreadSafeLayoutNode<'dom> {
fn as_image(&self) -> Option<(Option<Image>, PhysicalSize<f64>)> {
let node = self.to_threadsafe();
let (resource, metadata) = node.image_data()?;
let (resource, metadata) = self.image_data()?;
let (width, height) = resource
.as_ref()
.map(|image| {
@ -268,7 +263,7 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
.or_else(|| metadata.map(|metadata| (metadata.width, metadata.height)))
.unwrap_or((0, 0));
let (mut width, mut height) = (width as f64, height as f64);
if let Some(density) = node.image_density().filter(|density| *density != 1.) {
if let Some(density) = self.image_density().filter(|density| *density != 1.) {
width /= density;
height /= density;
}
@ -276,12 +271,11 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn as_svg(&self) -> Option<SVGElementData> {
self.to_threadsafe().svg_data()
self.svg_data()
}
fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)> {
let node = self.to_threadsafe();
let data = node.media_data()?;
let data = self.media_data()?;
let natural_size = if let Some(frame) = data.current_frame {
Some(PhysicalSize::new(frame.width.into(), frame.height.into()))
} else {
@ -295,8 +289,7 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)> {
let node = self.to_threadsafe();
let canvas_data = node.canvas_data()?;
let canvas_data = self.canvas_data()?;
let source = canvas_data.source;
Some((
CanvasInfo { source },
@ -305,8 +298,7 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)> {
let node = self.to_threadsafe();
match (node.iframe_pipeline_id(), node.iframe_browsing_context_id()) {
match (self.iframe_pipeline_id(), self.iframe_browsing_context_id()) {
(Some(pipeline_id), Some(browsing_context_id)) => {
Some((pipeline_id, browsing_context_id))
},
@ -315,8 +307,10 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn as_typeless_object_with_data_attribute(&self) -> Option<String> {
if LayoutNode::type_id(self) !=
ScriptLayoutNodeType::Element(LayoutElementType::HTMLObjectElement)
if self.type_id() !=
Some(ScriptLayoutNodeType::Element(
LayoutElementType::HTMLObjectElement,
))
{
return None;
}
@ -324,7 +318,7 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
// TODO: This is the what the legacy layout system did, but really if Servo
// supports any `<object>` that's an image, it should support those with URLs
// and `type` attributes with image mime types.
let element = self.to_threadsafe().as_element()?;
let element = self.as_element()?;
if element.get_attr(&ns!(), &local_name!("type")).is_some() {
return None;
}
@ -333,15 +327,11 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
.map(|string| string.to_owned())
}
fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues> {
self.to_threadsafe().style(context)
}
fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
if LayoutNode::layout_data(self).is_none() {
if ThreadSafeLayoutNode::layout_data(self).is_none() {
self.initialize_layout_data::<DOMLayoutData>();
}
LayoutNode::layout_data(self)
ThreadSafeLayoutNode::layout_data(self)
.unwrap()
.as_any()
.downcast_ref::<DOMLayoutData>()
@ -351,7 +341,7 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
}
fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> {
LayoutNode::layout_data(self).map(|data| {
ThreadSafeLayoutNode::layout_data(self).map(|data| {
data.as_any()
.downcast_ref::<DOMLayoutData>()
.unwrap()
@ -416,13 +406,13 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
fn repair_style(&self, context: &SharedStyleContext) {
let data = self.layout_data_mut();
if let Some(layout_object) = &*data.self_box.borrow() {
let style = self.to_threadsafe().style(context);
let style = self.style(context);
layout_object.repair_style(context, self, &style);
}
for pseudo_layout_data in data.pseudo_boxes.iter() {
if let Some(layout_box) = pseudo_layout_data.box_slot.borrow().as_ref() {
if let Some(node) = self.to_threadsafe().with_pseudo(pseudo_layout_data.pseudo) {
if let Some(node) = self.with_pseudo(pseudo_layout_data.pseudo) {
layout_box.repair_style(context, self, &node.style(context));
}
}