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