1use std::collections::HashSet;
6use std::ffi::c_void;
7use std::fmt;
8
9use embedder_traits::UntrustedNodeAddress;
10use js::rust::HandleValue;
11use layout_api::ElementsFromPointFlags;
12use rustc_hash::FxBuildHasher;
13use script_bindings::error::{Error, ErrorResult};
14use script_bindings::script_runtime::{CanGc, JSContext};
15use servo_arc::Arc;
16use servo_config::pref;
17use style::media_queries::MediaList;
18use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
19use style::stylesheets::scope_rule::ImplicitScopeRoot;
20use style::stylesheets::{Stylesheet, StylesheetContents};
21use stylo_atoms::Atom;
22use webrender_api::units::LayoutPoint;
23
24use super::bindings::trace::HashMapTracedValues;
25use crate::dom::bindings::cell::DomRefCell;
26use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
27use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
28use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMethods;
29use crate::dom::bindings::conversions::{ConversionResult, SafeFromJSValConvertible};
30use crate::dom::bindings::inheritance::Castable;
31use crate::dom::bindings::num::Finite;
32use crate::dom::bindings::root::{Dom, DomRoot};
33use crate::dom::css::stylesheetlist::StyleSheetListOwner;
34use crate::dom::element::Element;
35use crate::dom::html::htmlelement::HTMLElement;
36use crate::dom::node::{self, Node, VecPreOrderInsertionHelper};
37use crate::dom::shadowroot::ShadowRoot;
38use crate::dom::types::{CSSStyleSheet, EventTarget};
39use crate::dom::window::Window;
40use crate::stylesheet_set::StylesheetSetRef;
41
42#[derive(Clone, JSTraceable, MallocSizeOf)]
45#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
46pub(crate) enum StylesheetSource {
47 Element(Dom<Element>),
48 Constructed(Dom<CSSStyleSheet>),
49}
50
51impl StylesheetSource {
52 pub(crate) fn get_cssom_object(&self) -> Option<DomRoot<CSSStyleSheet>> {
53 match self {
54 StylesheetSource::Element(el) => el.upcast::<Node>().get_cssom_stylesheet(),
55 StylesheetSource::Constructed(ss) => Some(ss.as_rooted()),
56 }
57 }
58
59 pub(crate) fn is_a_valid_owner(&self) -> bool {
60 match self {
61 StylesheetSource::Element(el) => el.as_stylesheet_owner().is_some(),
62 StylesheetSource::Constructed(ss) => ss.is_constructed(),
63 }
64 }
65
66 pub(crate) fn is_constructed(&self) -> bool {
67 matches!(self, StylesheetSource::Constructed(_))
68 }
69}
70
71#[derive(Clone, JSTraceable, MallocSizeOf)]
72#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
73pub(crate) struct ServoStylesheetInDocument {
74 #[ignore_malloc_size_of = "Stylo"]
75 #[no_trace]
76 pub(crate) sheet: Arc<Stylesheet>,
77 pub(crate) owner: StylesheetSource,
81}
82
83impl stylo_malloc_size_of::MallocSizeOf for ServoStylesheetInDocument {
86 fn size_of(&self, ops: &mut stylo_malloc_size_of::MallocSizeOfOps) -> usize {
87 <ServoStylesheetInDocument as malloc_size_of::MallocSizeOf>::size_of(self, ops)
88 }
89}
90
91impl fmt::Debug for ServoStylesheetInDocument {
92 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
93 self.sheet.fmt(formatter)
94 }
95}
96
97impl PartialEq for ServoStylesheetInDocument {
98 fn eq(&self, other: &Self) -> bool {
99 Arc::ptr_eq(&self.sheet, &other.sheet)
100 }
101}
102
103impl ::style::stylesheets::StylesheetInDocument for ServoStylesheetInDocument {
104 fn enabled(&self) -> bool {
105 self.sheet.enabled()
106 }
107
108 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
109 self.sheet.media(guard)
110 }
111
112 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
113 self.sheet.contents(guard)
114 }
115
116 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
117 None
118 }
119}
120
121#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
123#[derive(Clone, JSTraceable, MallocSizeOf)]
124pub(crate) struct DocumentOrShadowRoot {
125 window: Dom<Window>,
126}
127
128impl DocumentOrShadowRoot {
129 pub(crate) fn new(window: &Window) -> Self {
130 Self {
131 window: Dom::from_ref(window),
132 }
133 }
134
135 #[expect(unsafe_code)]
136 pub(crate) fn element_from_point(
138 &self,
139 x: Finite<f64>,
140 y: Finite<f64>,
141 document_element: Option<DomRoot<Element>>,
142 has_browsing_context: bool,
143 ) -> Option<DomRoot<Element>> {
144 let x = *x as f32;
145 let y = *y as f32;
146 let viewport = self.window.viewport_details().size;
147
148 if !has_browsing_context {
149 return None;
150 }
151
152 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
153 return None;
154 }
155
156 let results = self
157 .window
158 .elements_from_point_query(LayoutPoint::new(x, y), ElementsFromPointFlags::empty());
159 let Some(result) = results.first() else {
160 return document_element;
161 };
162
163 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
166 let node = unsafe { node::from_untrusted_node_address(address) };
167 DomRoot::downcast::<Element>(node.clone()).or_else(|| {
168 let parent_node = node.GetParentNode()?;
169 if let Some(shadow_root) = parent_node.downcast::<ShadowRoot>() {
170 Some(shadow_root.Host())
171 } else {
172 node.GetParentElement()
173 }
174 })
175 }
176
177 #[expect(unsafe_code)]
178 pub(crate) fn elements_from_point(
180 &self,
181 x: Finite<f64>,
182 y: Finite<f64>,
183 document_element: Option<DomRoot<Element>>,
184 has_browsing_context: bool,
185 ) -> Vec<DomRoot<Element>> {
186 let x = *x as f32;
187 let y = *y as f32;
188 let viewport = self.window.viewport_details().size;
189
190 if !has_browsing_context {
191 return vec![];
192 }
193
194 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
196 return vec![];
197 }
198
199 let nodes = self
201 .window
202 .elements_from_point_query(LayoutPoint::new(x, y), ElementsFromPointFlags::FindAll);
203 let mut elements: Vec<DomRoot<Element>> = nodes
204 .iter()
205 .flat_map(|result| {
206 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
209 let node = unsafe { node::from_untrusted_node_address(address) };
210 DomRoot::downcast::<Element>(node)
211 })
212 .collect();
213
214 if let Some(root_element) = document_element {
216 if elements.last() != Some(&root_element) {
217 elements.push(root_element);
218 }
219 }
220
221 elements
223 }
224
225 pub(crate) fn get_active_element(
227 &self,
228 focused_element: Option<DomRoot<Element>>,
229 body: Option<DomRoot<HTMLElement>>,
230 document_element: Option<DomRoot<Element>>,
231 ) -> Option<DomRoot<Element>> {
232 match focused_element {
235 Some(element) => Some(element), None => match body {
237 Some(body) => Some(DomRoot::upcast(body)),
239 None => document_element,
240 },
241 }
242 }
243
244 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(
247 owner: StylesheetSource,
248 s: &Arc<Stylesheet>,
249 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
250 ) {
251 let guard = s.shared_lock.read();
252
253 stylesheets.remove_stylesheet(
255 None,
256 ServoStylesheetInDocument {
257 sheet: s.clone(),
258 owner,
259 },
260 &guard,
261 );
262 }
263
264 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_stylesheet(
268 owner: StylesheetSource,
269 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
270 sheet: Arc<Stylesheet>,
271 insertion_point: Option<ServoStylesheetInDocument>,
272 style_shared_lock: &StyleSharedRwLock,
273 ) {
274 debug_assert!(owner.is_a_valid_owner(), "Wat");
275
276 if owner.is_constructed() && !pref!(dom_adoptedstylesheet_enabled) {
277 return;
278 }
279
280 let sheet = ServoStylesheetInDocument { sheet, owner };
281
282 let guard = style_shared_lock.read();
283
284 match insertion_point {
285 Some(ip) => {
286 stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
287 },
288 None => {
289 stylesheets.append_stylesheet(None, sheet, &guard);
290 },
291 }
292 }
293
294 pub(crate) fn unregister_named_element(
296 &self,
297 id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
298 to_unregister: &Element,
299 id: &Atom,
300 ) {
301 debug!(
302 "Removing named element {:p}: {:p} id={}",
303 self, to_unregister, id
304 );
305 let mut id_map = id_map.borrow_mut();
306 let is_empty = match id_map.get_mut(id) {
307 None => false,
308 Some(elements) => {
309 let position = elements
310 .iter()
311 .position(|element| &**element == to_unregister)
312 .expect("This element should be in registered.");
313 elements.remove(position);
314 elements.is_empty()
315 },
316 };
317 if is_empty {
318 id_map.remove(id);
319 }
320 }
321
322 pub(crate) fn register_named_element(
324 &self,
325 id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
326 element: &Element,
327 id: &Atom,
328 root: DomRoot<Node>,
329 ) {
330 debug!("Adding named element {:p}: {:p} id={}", self, element, id);
331 assert!(
332 element.upcast::<Node>().is_in_a_document_tree() ||
333 element.upcast::<Node>().is_in_a_shadow_tree()
334 );
335 assert!(!id.is_empty());
336 let mut id_map = id_map.borrow_mut();
337 let elements = id_map.entry(id.clone()).or_default();
338 elements.insert_pre_order(element, &root);
339 }
340
341 fn set_adopted_stylesheet(
354 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
355 incoming_stylesheets: &[Dom<CSSStyleSheet>],
356 owner: &StyleSheetListOwner,
357 ) -> ErrorResult {
358 if !pref!(dom_adoptedstylesheet_enabled) {
359 return Ok(());
360 }
361
362 let owner_doc = match owner {
363 StyleSheetListOwner::Document(doc) => doc,
364 StyleSheetListOwner::ShadowRoot(root) => root.owner_doc(),
365 };
366
367 for sheet in incoming_stylesheets.iter() {
368 if !sheet.constructor_document_matches(owner_doc) {
371 return Err(Error::NotAllowed(None));
372 }
373 }
374
375 let mut stylesheet_remove_set = HashSet::with_capacity(adopted_stylesheets.len());
377
378 for sheet_to_remove in adopted_stylesheets.iter() {
383 if stylesheet_remove_set.insert(sheet_to_remove) {
385 owner.remove_stylesheet(
386 StylesheetSource::Constructed(sheet_to_remove.clone()),
387 &sheet_to_remove.style_stylesheet(),
388 );
389 sheet_to_remove.remove_adopter(owner);
390 }
391 }
392
393 let mut stylesheet_add_set = HashSet::with_capacity(incoming_stylesheets.len());
395
396 for sheet in incoming_stylesheets.iter() {
399 if !stylesheet_add_set.insert(sheet) {
401 owner.remove_stylesheet(
405 StylesheetSource::Constructed(sheet.clone()),
406 &sheet.style_stylesheet(),
407 );
408 } else {
409 sheet.add_adopter(owner.clone());
410 }
411
412 owner.append_constructed_stylesheet(sheet);
413 }
414
415 *adopted_stylesheets = incoming_stylesheets.to_vec();
416
417 Ok(())
418 }
419
420 pub(crate) fn set_adopted_stylesheet_from_jsval(
423 context: JSContext,
424 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
425 incoming_value: HandleValue,
426 owner: &StyleSheetListOwner,
427 can_gc: CanGc,
428 ) -> ErrorResult {
429 let maybe_stylesheets =
430 Vec::<DomRoot<CSSStyleSheet>>::safe_from_jsval(context, incoming_value, (), can_gc);
431
432 match maybe_stylesheets {
433 Ok(ConversionResult::Success(stylesheets)) => {
434 rooted_vec!(let stylesheets <- stylesheets.to_owned().iter().map(|s| s.as_traced()));
435
436 DocumentOrShadowRoot::set_adopted_stylesheet(
437 adopted_stylesheets,
438 &stylesheets,
439 owner,
440 )
441 },
442 Ok(ConversionResult::Failure(msg)) => Err(Error::Type(msg.into_owned())),
443 Err(_) => Err(Error::Type(
444 c"The provided value is not a sequence of 'CSSStylesheet'.".to_owned(),
445 )),
446 }
447 }
448
449 pub(crate) fn get_fullscreen_element(
451 node: &Node,
452 fullscreen_element: Option<DomRoot<Element>>,
453 ) -> Option<DomRoot<Element>> {
454 if let Some(shadow_root) = node.downcast::<ShadowRoot>() {
456 if !shadow_root.Host().is_connected() {
457 return None;
458 }
459 }
460
461 let retargeted = fullscreen_element?
463 .upcast::<EventTarget>()
464 .retarget(node.upcast());
465 let candidate = DomRoot::downcast::<Element>(retargeted).unwrap();
467
468 if *candidate
470 .upcast::<Node>()
471 .GetRootNode(&GetRootNodeOptions::empty()) ==
472 *node
473 {
474 return Some(candidate);
475 }
476
477 None
479 }
480}