Skip to main content

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, ServoLayoutDomTypeBundle, 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(Some(format!(
56                "'{selector}' is an invalid selector"
57            ))));
58        };
59
60        // Step 3. Return the result of match a selector against a tree with selector
61        // and node’s root using scoping root node.
62        query_selector::<ServoDangerousStyleElement<'dom>, Query>(
63            self,
64            &selector_list,
65            &mut result,
66            MayUseInvalidation::No,
67        );
68
69        Ok(result)
70    }
71}
72
73impl<'dom> From<LayoutDom<'dom, Node>> for ServoDangerousStyleNode<'dom> {
74    fn from(node: LayoutDom<'dom, Node>) -> Self {
75        Self { node }
76    }
77}
78
79impl<'dom> DangerousStyleNode<'dom> for ServoDangerousStyleNode<'dom> {
80    type ConcreteTypeBundle = ServoLayoutDomTypeBundle<'dom>;
81
82    fn layout_node(&self) -> ServoLayoutNode<'dom> {
83        self.node.into()
84    }
85}
86
87impl NodeInfo for ServoDangerousStyleNode<'_> {
88    fn is_element(&self) -> bool {
89        self.node.is_element_for_layout()
90    }
91
92    fn is_text_node(&self) -> bool {
93        self.node.is_text_node_for_layout()
94    }
95}
96
97impl<'dom> TNode for ServoDangerousStyleNode<'dom> {
98    type ConcreteDocument = ServoDangerousStyleDocument<'dom>;
99    type ConcreteElement = ServoDangerousStyleElement<'dom>;
100    type ConcreteShadowRoot = ServoDangerousStyleShadowRoot<'dom>;
101
102    fn parent_node(&self) -> Option<Self> {
103        self.node.parent_node_ref().map(Into::into)
104    }
105
106    fn first_child(&self) -> Option<Self> {
107        self.node.first_child_ref().map(Into::into)
108    }
109
110    fn last_child(&self) -> Option<Self> {
111        self.node.last_child_ref().map(Into::into)
112    }
113
114    fn prev_sibling(&self) -> Option<Self> {
115        self.node.prev_sibling_ref().map(Into::into)
116    }
117
118    fn next_sibling(&self) -> Option<Self> {
119        self.node.next_sibling_ref().map(Into::into)
120    }
121
122    fn owner_doc(&self) -> Self::ConcreteDocument {
123        self.node.owner_doc_for_layout().into()
124    }
125
126    fn traversal_parent(&self) -> Option<ServoDangerousStyleElement<'dom>> {
127        Some(self.node.traversal_parent()?.into())
128    }
129
130    fn opaque(&self) -> style::dom::OpaqueNode {
131        self.node.opaque()
132    }
133
134    fn debug_id(self) -> usize {
135        self.opaque().0
136    }
137
138    fn as_element(&self) -> Option<ServoDangerousStyleElement<'dom>> {
139        Some(self.node.downcast()?.into())
140    }
141
142    fn as_document(&self) -> Option<ServoDangerousStyleDocument<'dom>> {
143        self.node.downcast().map(Into::into)
144    }
145
146    fn as_shadow_root(&self) -> Option<ServoDangerousStyleShadowRoot<'dom>> {
147        self.node.downcast().map(Into::into)
148    }
149
150    fn is_in_document(&self) -> bool {
151        unsafe { self.node.get_flag(NodeFlags::IS_IN_A_DOCUMENT_TREE) }
152    }
153}