mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
script: Rewrite the layout DOM wrappers (#44114)
This change reworks the layout DOM wrappers so that they are simpler and easier to reason about. The main changes here: **Combine layout wrappers into one interface:** - `LayoutNode`/`ThreadSafeLayoutNode` is combined into `LayoutNode`: The idea here is that `LayoutNode` is always thread-safe when used in layout as long as no `unsafe` calls are used. These interfaces only expose what is necessary for layout. - `LayoutElement`/`ThreadSafeLayoutElement` is combined into `LayoutElement`: See above. **Expose two new interfaces to be used *only* with `stylo` and `selectors`:** `DangerousStyleNode` and `DangerousStyleElement`. `stylo` and `selectors` have a different way of ensuring safety that is incompatible with Servo's layout (access all of the DOM tree anywhere, but ensure that writing only happens from a single-thread). These types only implement things like `TElement`, `TNode` and are not intended to be used by layout at all. All traits and implementations are moved to files that are named after the struct or trait inside them, in order to better understand what one is looking at. The main goals here are: - Make it easier to reason about the safe use of the DOM APIs. - Remove the interdependencies between the `stylo` and `selectors` interface implementations and the layout interface. This helps with the first point as well and makes it simpler to know where a method is implemented. - Reduce the amount of code. - Make it possible to eliminate `TrustedNodeAddress` in the future. - Document and bring the method naming up to modern Rust conventions. This is a lot of code changes, but is very well tested by the WPT tests. Unfortunately, it is difficult to make a change like this iteratively. In addition, this new design comes with new documentation at servo/book#225. Testing: This should not change behavior so should be covered by existing WPT tests. Signed-off-by: Martin Robinson <mrobinson@fastmail.fm>
This commit is contained in:
@@ -26,11 +26,10 @@ use js::context::{JSContext, NoGC};
|
||||
use js::jsapi::JSObject;
|
||||
use js::rust::HandleObject;
|
||||
use keyboard_types::Modifiers;
|
||||
use layout_api::wrapper_traits::SharedSelection;
|
||||
use layout_api::{
|
||||
AxesOverflow, BoxAreaType, CSSPixelRectIterator, GenericLayoutData, HTMLCanvasData,
|
||||
HTMLMediaData, LayoutElementType, LayoutNodeType, PhysicalSides, SVGElementData,
|
||||
TrustedNodeAddress, with_layout_state,
|
||||
SharedSelection, TrustedNodeAddress, with_layout_state,
|
||||
};
|
||||
use libc::{self, c_void, uintptr_t};
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
@@ -131,7 +130,7 @@ use crate::dom::text::Text;
|
||||
use crate::dom::types::{CDATASection, KeyboardEvent};
|
||||
use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
|
||||
use crate::dom::window::Window;
|
||||
use crate::layout_dom::ServoLayoutNode;
|
||||
use crate::layout_dom::ServoDangerousStyleNode;
|
||||
use crate::script_runtime::CanGc;
|
||||
use crate::script_thread::ScriptThread;
|
||||
|
||||
@@ -1542,12 +1541,12 @@ impl Node {
|
||||
|
||||
let first_matching_element = with_layout_state(|| {
|
||||
let layout_node = unsafe { traced_node.to_layout() };
|
||||
ServoLayoutNode::from_layout_dom(layout_node)
|
||||
ServoDangerousStyleNode::from(layout_node)
|
||||
.scope_match_a_selectors_string::<QueryFirst>(document_url, &selectors.str())
|
||||
})?;
|
||||
|
||||
Ok(first_matching_element
|
||||
.map(|element| DomRoot::from_ref(unsafe { element.to_layout_dom().as_ref() })))
|
||||
.map(|element| DomRoot::from_ref(unsafe { element.layout_dom().as_ref() })))
|
||||
}
|
||||
|
||||
/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall>
|
||||
@@ -1562,12 +1561,12 @@ impl Node {
|
||||
let traced_node = Dom::from_ref(self);
|
||||
let matching_elements = with_layout_state(|| {
|
||||
let layout_node = unsafe { traced_node.to_layout() };
|
||||
ServoLayoutNode::from_layout_dom(layout_node)
|
||||
ServoDangerousStyleNode::from(layout_node)
|
||||
.scope_match_a_selectors_string::<QueryAll>(document_url, &selectors.str())
|
||||
})?;
|
||||
let iter = matching_elements
|
||||
.into_iter()
|
||||
.map(|element| DomRoot::from_ref(unsafe { element.to_layout_dom().as_ref() }))
|
||||
.map(|element| DomRoot::from_ref(unsafe { element.layout_dom().as_ref() }))
|
||||
.map(DomRoot::upcast::<Node>);
|
||||
|
||||
// NodeList::new_simple_list immediately collects the iterator, so we're not leaking LayoutDom
|
||||
@@ -2112,6 +2111,18 @@ impl<'dom> LayoutDom<'dom, Node> {
|
||||
parent
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn traversal_parent(self) -> Option<LayoutDom<'dom, Element>> {
|
||||
if let Some(assigned_slot) = self.assigned_slot_for_layout() {
|
||||
return Some(assigned_slot.upcast());
|
||||
}
|
||||
let parent = self.parent_node_ref()?;
|
||||
if let Some(shadow) = parent.downcast::<ShadowRoot>() {
|
||||
return Some(shadow.get_host_for_layout());
|
||||
};
|
||||
parent.downcast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[expect(unsafe_code)]
|
||||
pub(crate) fn first_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
|
||||
|
||||
Reference in New Issue
Block a user