script/layout_dom/
servo_dangerous_style_node.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![expect(unsafe_code)]
6#![deny(missing_docs)]
7
8use layout_api::DangerousStyleNode;
9use script_bindings::error::Fallible;
10use servo_arc::Arc;
11use style;
12use style::dom::{NodeInfo, TNode};
13use style::dom_apis::{MayUseInvalidation, SelectorQuery, query_selector};
14use style::selector_parser::SelectorParser;
15use style::stylesheets::UrlExtraData;
16use url::Url;
17
18use super::{ServoDangerousStyleDocument, ServoDangerousStyleShadowRoot};
19use crate::dom::bindings::error::Error;
20use crate::dom::bindings::root::LayoutDom;
21use crate::dom::node::{Node, NodeFlags};
22use crate::layout_dom::{ServoDangerousStyleElement, ServoLayoutNode};
23
24/// A wrapper around [`LayoutDom<_, Node>`] to be used with `stylo` and `selectors`.
25///
26/// Note: This should only be used for `stylo` or `selectors interaction.
27#[derive(Copy, Clone, Debug, Eq, PartialEq)]
28#[repr(transparent)]
29pub struct ServoDangerousStyleNode<'dom> {
30    pub(crate) node: LayoutDom<'dom, Node>,
31}
32
33unsafe impl Send for ServoDangerousStyleNode<'_> {}
34unsafe impl Sync for ServoDangerousStyleNode<'_> {}
35
36impl<'dom> ServoDangerousStyleNode<'dom> {
37    /// <https://dom.spec.whatwg.org/#scope-match-a-selectors-string>
38    pub(crate) fn scope_match_a_selectors_string<Query>(
39        self,
40        document_url: Arc<Url>,
41        selector: &str,
42    ) -> Fallible<Query::Output>
43    where
44        Query: SelectorQuery<ServoDangerousStyleElement<'dom>>,
45        Query::Output: Default,
46    {
47        let mut result = Query::Output::default();
48
49        // Step 1. Let selector be the result of parse a selector selectors.
50        let selector_or_error =
51            SelectorParser::parse_author_origin_no_namespace(selector, &UrlExtraData(document_url));
52
53        // Step 2. If selector is failure, then throw a "SyntaxError" DOMException.
54        let Ok(selector_list) = selector_or_error else {
55            return Err(Error::Syntax(None));
56        };
57
58        // Step 3. Return the result of match a selector against a tree with selector
59        // and node’s root using scoping root node.
60        query_selector::<ServoDangerousStyleElement<'dom>, Query>(
61            self,
62            &selector_list,
63            &mut result,
64            MayUseInvalidation::No,
65        );
66
67        Ok(result)
68    }
69}
70
71impl<'dom> From<LayoutDom<'dom, Node>> for ServoDangerousStyleNode<'dom> {
72    fn from(node: LayoutDom<'dom, Node>) -> Self {
73        Self { node }
74    }
75}
76
77impl<'dom> DangerousStyleNode<'dom> for ServoDangerousStyleNode<'dom> {
78    type ConcreteLayoutNode = ServoLayoutNode<'dom>;
79
80    fn layout_node(&self) -> Self::ConcreteLayoutNode {
81        self.node.into()
82    }
83}
84
85impl NodeInfo for ServoDangerousStyleNode<'_> {
86    fn is_element(&self) -> bool {
87        self.node.is_element_for_layout()
88    }
89
90    fn is_text_node(&self) -> bool {
91        self.node.is_text_node_for_layout()
92    }
93}
94
95impl<'dom> TNode for ServoDangerousStyleNode<'dom> {
96    type ConcreteDocument = ServoDangerousStyleDocument<'dom>;
97    type ConcreteElement = ServoDangerousStyleElement<'dom>;
98    type ConcreteShadowRoot = ServoDangerousStyleShadowRoot<'dom>;
99
100    fn parent_node(&self) -> Option<Self> {
101        self.node.parent_node_ref().map(Into::into)
102    }
103
104    fn first_child(&self) -> Option<Self> {
105        self.node.first_child_ref().map(Into::into)
106    }
107
108    fn last_child(&self) -> Option<Self> {
109        self.node.last_child_ref().map(Into::into)
110    }
111
112    fn prev_sibling(&self) -> Option<Self> {
113        self.node.prev_sibling_ref().map(Into::into)
114    }
115
116    fn next_sibling(&self) -> Option<Self> {
117        self.node.next_sibling_ref().map(Into::into)
118    }
119
120    fn owner_doc(&self) -> Self::ConcreteDocument {
121        self.node.owner_doc_for_layout().into()
122    }
123
124    fn traversal_parent(&self) -> Option<ServoDangerousStyleElement<'dom>> {
125        Some(self.node.traversal_parent()?.into())
126    }
127
128    fn opaque(&self) -> style::dom::OpaqueNode {
129        self.node.opaque()
130    }
131
132    fn debug_id(self) -> usize {
133        self.opaque().0
134    }
135
136    fn as_element(&self) -> Option<ServoDangerousStyleElement<'dom>> {
137        Some(self.node.downcast()?.into())
138    }
139
140    fn as_document(&self) -> Option<ServoDangerousStyleDocument<'dom>> {
141        self.node.downcast().map(Into::into)
142    }
143
144    fn as_shadow_root(&self) -> Option<ServoDangerousStyleShadowRoot<'dom>> {
145        self.node.downcast().map(Into::into)
146    }
147
148    fn is_in_document(&self) -> bool {
149        unsafe { self.node.get_flag(NodeFlags::IS_IN_A_DOCUMENT_TREE) }
150    }
151}