1use std::collections::HashSet;
6use std::ffi::c_void;
7use std::fmt;
8
9use embedder_traits::UntrustedNodeAddress;
10use js::context::JSContext;
11use layout_api::HitTestFlags;
12use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
13use script_bindings::codegen::GenericBindings::ShadowRootBinding::ShadowRootMethods;
14use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
15use script_bindings::error::{Error, ErrorResult};
16use servo_arc::Arc;
17use servo_config::pref;
18use style::media_queries::MediaList;
19use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
20use style::stylesheets::scope_rule::ImplicitScopeRoot;
21use style::stylesheets::{Stylesheet, StylesheetContents};
22use webrender_api::units::LayoutPoint;
23
24use crate::css::stylesheet_set::StylesheetSetRef;
25use crate::dom::Document;
26use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
27use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
28use crate::dom::bindings::inheritance::Castable;
29use crate::dom::bindings::num::Finite;
30use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
31use crate::dom::css::stylesheetlist::StyleSheetListOwner;
32use crate::dom::customelementregistry::CustomElementRegistry;
33use crate::dom::element::Element;
34use crate::dom::node::{self, Node};
35use crate::dom::types::{CSSStyleSheet, EventTarget, ShadowRoot};
36use crate::dom::window::Window;
37
38#[derive(Clone, JSTraceable, MallocSizeOf)]
41#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
42pub(crate) enum StylesheetSource {
43 Element(Dom<Element>),
44 Constructed(Dom<CSSStyleSheet>),
45}
46
47impl StylesheetSource {
48 pub(crate) fn get_cssom_object(&self, cx: &mut JSContext) -> Option<DomRoot<CSSStyleSheet>> {
49 match self {
50 StylesheetSource::Element(el) => el.upcast::<Node>().get_cssom_stylesheet(cx),
51 StylesheetSource::Constructed(ss) => Some(ss.as_rooted()),
52 }
53 }
54
55 pub(crate) fn is_a_valid_owner(&self) -> bool {
56 match self {
57 StylesheetSource::Element(el) => el.as_stylesheet_owner().is_some(),
58 StylesheetSource::Constructed(ss) => ss.is_constructed(),
59 }
60 }
61
62 pub(crate) fn is_constructed(&self) -> bool {
63 matches!(self, StylesheetSource::Constructed(_))
64 }
65}
66
67#[derive(Clone, JSTraceable, MallocSizeOf)]
68#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
69pub(crate) struct ServoStylesheetInDocument {
70 #[ignore_malloc_size_of = "Stylo"]
71 #[no_trace]
72 pub(crate) sheet: Arc<Stylesheet>,
73 pub(crate) owner: StylesheetSource,
77}
78
79impl stylo_malloc_size_of::MallocSizeOf for ServoStylesheetInDocument {
82 fn size_of(&self, ops: &mut stylo_malloc_size_of::MallocSizeOfOps) -> usize {
83 <ServoStylesheetInDocument as malloc_size_of::MallocSizeOf>::size_of(self, ops)
84 }
85}
86
87impl fmt::Debug for ServoStylesheetInDocument {
88 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
89 self.sheet.fmt(formatter)
90 }
91}
92
93impl PartialEq for ServoStylesheetInDocument {
94 fn eq(&self, other: &Self) -> bool {
95 Arc::ptr_eq(&self.sheet, &other.sheet)
96 }
97}
98
99impl ::style::stylesheets::StylesheetInDocument for ServoStylesheetInDocument {
100 fn enabled(&self) -> bool {
101 self.sheet.enabled()
102 }
103
104 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
105 self.sheet.media(guard)
106 }
107
108 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
109 self.sheet.contents(guard)
110 }
111
112 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
113 None
114 }
115}
116
117#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
119#[derive(JSTraceable, MallocSizeOf)]
120pub(crate) struct DocumentOrShadowRoot {
121 window: Dom<Window>,
122 custom_element_registry: MutNullableDom<CustomElementRegistry>,
123}
124
125impl DocumentOrShadowRoot {
126 pub(crate) fn new(window: &Window) -> Self {
127 Self {
128 window: Dom::from_ref(window),
129 custom_element_registry: MutNullableDom::new(None),
130 }
131 }
132
133 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
135 self.custom_element_registry.get()
136 }
137
138 pub(crate) fn set_custom_element_registry(&self, registry: Option<&CustomElementRegistry>) {
139 self.custom_element_registry.set(registry);
140 }
141
142 pub(crate) fn retarget_hit_test_result(
145 &self,
146 this: &Node,
147 node: &Node,
148 ) -> Option<DomRoot<Element>> {
149 let retargeted_node =
150 DomRoot::downcast::<Node>(node.upcast::<EventTarget>().retarget(this.upcast()))?;
151 DomRoot::downcast::<Element>(retargeted_node.clone()).or_else(|| {
152 let parent_node = retargeted_node.GetParentNode()?;
153
154 if let Some(shadow_root) = parent_node.downcast::<ShadowRoot>() {
159 Some(shadow_root.Host())
160 } else {
161 retargeted_node.GetParentElement()
162 }
163 })
164 }
165
166 #[expect(unsafe_code)]
168 pub(crate) fn element_from_point(
169 &self,
170 this: &Node,
171 x: Finite<f64>,
172 y: Finite<f64>,
173 document_element: Option<DomRoot<Element>>,
174 has_browsing_context: bool,
175 ) -> Option<DomRoot<Element>> {
176 let x = *x as f32;
177 let y = *y as f32;
178 let viewport = self.window.viewport_details().size;
179
180 if !has_browsing_context {
181 return None;
182 }
183
184 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
185 return None;
186 }
187
188 let flags = HitTestFlags::empty();
189 let result = self
190 .window
191 .elements_from_point_query(flags, LayoutPoint::new(x, y));
192 let Some(result) = result.items.first() else {
193 return document_element;
194 };
195
196 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
199 let node = unsafe { node::from_untrusted_node_address(address) };
200
201 self.retarget_hit_test_result(this, &node)
202 }
203
204 #[expect(unsafe_code)]
206 pub(crate) fn elements_from_point(
207 &self,
208 this: &Node,
209 x: Finite<f64>,
210 y: Finite<f64>,
211 document_element: Option<DomRoot<Element>>,
212 has_browsing_context: bool,
213 ) -> Vec<DomRoot<Element>> {
214 let x = *x as f32;
215 let y = *y as f32;
216 let viewport = self.window.viewport_details().size;
217
218 if !has_browsing_context {
219 return vec![];
220 }
221
222 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
224 return vec![];
225 }
226
227 let flags = HitTestFlags::empty();
233 let result = self
234 .window
235 .elements_from_point_query(flags, LayoutPoint::new(x, y));
236
237 let mut elements: Vec<_> = result
238 .items
239 .iter()
240 .flat_map(|result| {
241 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
244 let node = unsafe { node::from_untrusted_node_address(address) };
245 self.retarget_hit_test_result(this, &node)
246 })
247 .collect();
248
249 let mut last_seen = None;
252 elements.retain(|element| {
253 if Some(element) == last_seen.as_ref() {
254 return false;
255 }
256 last_seen = Some(element.clone());
257 true
258 });
259
260 if let Some(root_element) = document_element &&
263 elements.last() != Some(&root_element)
264 {
265 elements.push(root_element);
266 }
267
268 elements
270 }
271
272 pub(crate) fn active_element(&self, this: &Node) -> Option<DomRoot<Element>> {
274 let document = self.window.Document();
276 let candidate = document
277 .focus_handler()
278 .focused_area()
279 .dom_anchor(&document);
280
281 let candidate =
286 DomRoot::downcast::<Node>(candidate.upcast::<EventTarget>().retarget(this.upcast()))?;
287
288 if this != &*candidate.GetRootNode(&GetRootNodeOptions::empty()) {
290 return None;
291 }
292
293 if let Some(candidate) = DomRoot::downcast::<Element>(candidate.clone()) {
295 return Some(candidate);
296 }
297 assert!(candidate.is::<Document>());
298
299 if let Some(body) = document.GetBody() {
301 return Some(DomRoot::upcast(body));
302 }
303
304 if let Some(document_element) = document.GetDocumentElement() {
306 return Some(document_element);
307 }
308
309 None
311 }
312
313 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(
316 owner: StylesheetSource,
317 s: &Arc<Stylesheet>,
318 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
319 ) {
320 let guard = s.shared_lock.read();
321
322 stylesheets.remove_stylesheet(
324 None,
325 ServoStylesheetInDocument {
326 sheet: s.clone(),
327 owner,
328 },
329 &guard,
330 );
331 }
332
333 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_stylesheet(
337 owner: StylesheetSource,
338 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
339 sheet: Arc<Stylesheet>,
340 insertion_point: Option<ServoStylesheetInDocument>,
341 style_shared_lock: &StyleSharedRwLock,
342 ) {
343 debug_assert!(owner.is_a_valid_owner(), "Wat");
344
345 if owner.is_constructed() && !pref!(dom_adoptedstylesheet_enabled) {
346 return;
347 }
348
349 let sheet = ServoStylesheetInDocument { sheet, owner };
350
351 let guard = style_shared_lock.read();
352
353 match insertion_point {
354 Some(ip) => {
355 stylesheets.insert_stylesheet_before(sheet, ip, &guard);
356 },
357 None => {
358 stylesheets.append_stylesheet(sheet, &guard);
359 },
360 }
361 }
362
363 fn set_adopted_stylesheets(
370 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
371 incoming_stylesheets: &[Dom<CSSStyleSheet>],
372 owner: &StyleSheetListOwner,
373 ) -> ErrorResult {
374 if !pref!(dom_adoptedstylesheet_enabled) {
375 return Ok(());
376 }
377
378 let owner_doc = match owner {
379 StyleSheetListOwner::Document(doc) => doc,
380 StyleSheetListOwner::ShadowRoot(root) => root.owner_doc(),
381 };
382
383 for sheet in incoming_stylesheets {
384 if !sheet.constructor_document_matches(owner_doc) {
387 return Err(Error::NotAllowed(Some(
388 "Stylesheet is not constructed or its constructor document does not match"
389 .into(),
390 )));
391 }
392 }
393
394 let mut stylesheet_remove_set = HashSet::with_capacity(adopted_stylesheets.len());
396
397 for sheet_to_remove in adopted_stylesheets.iter() {
400 if stylesheet_remove_set.insert(sheet_to_remove) {
402 owner.remove_stylesheet(
403 StylesheetSource::Constructed(sheet_to_remove.clone()),
404 &sheet_to_remove.style_stylesheet(),
405 );
406 sheet_to_remove.remove_adopter(owner);
407 }
408 }
409
410 let mut stylesheet_add_set = HashSet::with_capacity(incoming_stylesheets.len());
412
413 for sheet in incoming_stylesheets {
416 if !stylesheet_add_set.insert(sheet) {
418 owner.remove_stylesheet(
422 StylesheetSource::Constructed(sheet.clone()),
423 &sheet.style_stylesheet(),
424 );
425 } else {
426 sheet.add_adopter(owner.clone());
427 }
428
429 owner.append_constructed_stylesheet(sheet);
430 }
431
432 *adopted_stylesheets = incoming_stylesheets.to_vec();
433 Ok(())
434 }
435
436 pub(crate) fn on_set_adopted_stylesheets(
438 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
439 value: &CSSStyleSheet,
440 index: u32,
441 owner: &StyleSheetListOwner,
442 ) -> ErrorResult {
443 rooted_vec!(let mut incoming_stylesheets <- adopted_stylesheets.iter().cloned());
444 incoming_stylesheets.insert(index as usize, Dom::from_ref(value));
445 Self::set_adopted_stylesheets(adopted_stylesheets, &incoming_stylesheets, owner)
446 }
447
448 pub(crate) fn on_delete_adopted_stylesheets(
450 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
451 index: u32,
452 owner: &StyleSheetListOwner,
453 ) -> ErrorResult {
454 rooted_vec!(let mut incoming_stylesheets <- adopted_stylesheets.iter().cloned());
455 incoming_stylesheets.remove(index as usize);
456 Self::set_adopted_stylesheets(adopted_stylesheets, &incoming_stylesheets, owner)
457 }
458}