/
githubmirror
/
servo
Обзор
Документация
Войти
/
githubmirror
/
servo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
components/script/layout_dom/servo_layout_element.rs
256 строк
9 KB
Martin Robinson
layout_api: Use a "type bundle" to encapsulate all layout DOM types (#44454)
25 апр 2026, 12:21
Не верифицирован
25 апр 2026, 12:21
1a9808d
Код
Авторство
О чём код?
/* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ #![expect(unsafe_code)] #![deny(missing_docs)] use std::fmt; use std::hash::Hash; use html5ever::{LocalName, Namespace, local_name, ns}; use layout_api::{ LayoutDataTrait, LayoutElement, LayoutNode, LayoutNodeType, PseudoElementChain, StyleData, }; use servo_arc::Arc; use style::attr::AttrValue; use style::context::SharedStyleContext; use style::data::{ElementDataMut, ElementDataRef}; use style::properties::ComputedValues; use style::selector_parser::{PseudoElement, PseudoElementCascadeType}; use style::stylist::RuleInclusion; use crate::dom::bindings::root::LayoutDom; use crate::dom::element::Element; use crate::dom::node::{Node, NodeFlags}; use crate::layout_dom::{ ServoDangerousStyleElement, ServoDangerousStyleShadowRoot, ServoLayoutDomTypeBundle, ServoLayoutNode, }; impl fmt::Debug for LayoutDom<'_, Element> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<{}", self.local_name())?; if let Some(id) = unsafe { (*self.id_attribute()).as_ref() } { write!(f, " id={id}")?; } write!(f, "> ({:#x})", self.upcast::<Node>().opaque().0) } } /// An implementation of [`LayoutElement`] for Servo's `script` crate. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct ServoLayoutElement<'dom> { /// The wrapped private DOM Element. pub(super) element: LayoutDom<'dom, Element>, /// The possibly nested [`PseudoElementChain`] for this element. pub(super) pseudo_element_chain: PseudoElementChain, } unsafe impl Send for ServoLayoutElement<'_> {} unsafe impl Sync for ServoLayoutElement<'_> {} impl<'dom> ServoLayoutElement<'dom> { pub(super) fn is_html_element(&self) -> bool { self.element.is_html_element() } /// The shadow root that this [`ServoLayoutElement`] is a host of, if it has one. /// /// Note: This should *not* be exposed to layout as it allows access to an ancestor element. pub(super) fn shadow_root(&self) -> Option<ServoDangerousStyleShadowRoot<'dom>> { self.element.get_shadow_root_for_layout().map(Into::into) } } impl<'dom> From<LayoutDom<'dom, Element>> for ServoLayoutElement<'dom> { fn from(element: LayoutDom<'dom, Element>) -> Self { Self { element, pseudo_element_chain: Default::default(), } } } impl<'dom> LayoutElement<'dom> for ServoLayoutElement<'dom> { type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>; fn with_pseudo(&self, pseudo_element: PseudoElement) -> Option<Self> { if pseudo_element.is_eager() && self.element_data() .styles .pseudos .get(&pseudo_element) .is_none() { return None; } if pseudo_element == PseudoElement::DetailsContent && (self.element.local_name() != &local_name!("details") || self.element.namespace() != &ns!(html) || self.attribute(&ns!(), &local_name!("open")).is_none()) { return None; } // These pseudo-element type cannot be nested. if !self.pseudo_element_chain.is_empty() { assert!(!pseudo_element.is_eager()); assert!(pseudo_element != PseudoElement::DetailsContent); } Some(Self { element: self.element, pseudo_element_chain: self.pseudo_element_chain.with_pseudo(pseudo_element), }) } fn pseudo_element_chain(&self) -> PseudoElementChain { self.pseudo_element_chain } fn as_node(&self) -> ServoLayoutNode<'dom> { ServoLayoutNode { node: self.element.upcast(), pseudo_element_chain: self.pseudo_element_chain, } } fn initialize_style_and_layout_data<RequestedLayoutDataType: LayoutDataTrait>(&self) { if self.element.style_data().is_none() { unsafe { self.element.initialize_style_data() }; } let node = self.element.upcast::<Node>(); if node.layout_data().is_none() { unsafe { node.initialize_layout_data(Box::<RequestedLayoutDataType>::default()) }; } } fn unset_snapshot_flags(&self) { unsafe { self.as_node() .node .set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false); } } fn set_has_snapshot(&self) { unsafe { self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true); } } fn style_data(self) -> Option<&'dom StyleData> { self.element.style_data() } fn element_data(&self) -> ElementDataRef<'dom> { self.style_data() .expect("Unstyled layout node?") .element_data .borrow() } fn element_data_mut(&self) -> ElementDataMut<'dom> { self.style_data() .expect("Unstyled layout node?") .element_data .borrow_mut() } fn style(&self, context: &SharedStyleContext) -> Arc<ComputedValues> { let get_style_for_pseudo_element = |data: &ElementDataRef<'_>, base_style: &Arc<ComputedValues>, pseudo_element: PseudoElement| { // Precompute non-eagerly-cascaded pseudo-element styles if not // cached before. match pseudo_element.cascade_type() { // Already computed during the cascade. PseudoElementCascadeType::Eager => { data.styles.pseudos.get(&pseudo_element).unwrap().clone() }, PseudoElementCascadeType::Precomputed => context .stylist .precomputed_values_for_pseudo::<ServoDangerousStyleElement<'dom>>( &context.guards, &pseudo_element, Some(base_style), ), PseudoElementCascadeType::Lazy => { context .stylist .lazily_compute_pseudo_element_style( &context.guards, unsafe { self.dangerous_style_element() }, &pseudo_element, RuleInclusion::All, base_style, /* is_probe = */ false, /* matching_func = */ None, ) .unwrap() }, } }; let data = self.element_data(); let element_style = data.styles.primary(); let pseudo_element_chain = self.pseudo_element_chain(); let primary_pseudo_style = match pseudo_element_chain.primary { Some(pseudo_element) => { get_style_for_pseudo_element(&data, element_style, pseudo_element) }, None => return element_style.clone(), }; match pseudo_element_chain.secondary { Some(pseudo_element) => { get_style_for_pseudo_element(&data, &primary_pseudo_style, pseudo_element) }, None => primary_pseudo_style, } } fn type_id(&self) -> Option<LayoutNodeType> { self.as_node().type_id() } unsafe fn dangerous_style_element(self) -> ServoDangerousStyleElement<'dom> { self.element.into() } fn local_name(&self) -> &LocalName { self.element.local_name() } fn attribute(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> { self.element.get_attr_for_layout(namespace, name) } fn attribute_as_str<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> { self.element.get_attr_val_for_layout(namespace, name) } fn is_shadow_host(&self) -> bool { self.element.get_shadow_root_for_layout().is_some() } fn is_body_element_of_html_element_root(&self) -> bool { self.element.is_body_element_of_html_element_root() } fn is_html_element_in_html_document(&self) -> bool { self.element.is_html_element() && self.element .upcast::<Node>() .owner_doc_for_layout() .is_html_document_for_layout() } fn is_root(&self) -> bool { self.element.is_root() } }