1use std::collections::HashSet;
6use std::ffi::c_void;
7use std::fmt;
8
9use embedder_traits::UntrustedNodeAddress;
10use js::context::JSContext;
11use js::conversions::FromJSValConvertible;
12use js::rust::HandleValue;
13use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
14use script_bindings::codegen::GenericBindings::ShadowRootBinding::ShadowRootMethods;
15use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
16use script_bindings::error::{Error, ErrorResult};
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 webrender_api::units::LayoutPoint;
24
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::conversions::ConversionResult;
29use crate::dom::bindings::inheritance::Castable;
30use crate::dom::bindings::num::Finite;
31use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
32use crate::dom::css::stylesheetlist::StyleSheetListOwner;
33use crate::dom::customelementregistry::CustomElementRegistry;
34use crate::dom::element::Element;
35use crate::dom::node::{self, Node};
36use crate::dom::types::{CSSStyleSheet, EventTarget, ShadowRoot};
37use crate::dom::window::Window;
38use crate::stylesheet_set::StylesheetSetRef;
39
40#[derive(Clone, JSTraceable, MallocSizeOf)]
43#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
44pub(crate) enum StylesheetSource {
45 Element(Dom<Element>),
46 Constructed(Dom<CSSStyleSheet>),
47}
48
49impl StylesheetSource {
50 pub(crate) fn get_cssom_object(&self) -> Option<DomRoot<CSSStyleSheet>> {
51 match self {
52 StylesheetSource::Element(el) => el.upcast::<Node>().get_cssom_stylesheet(),
53 StylesheetSource::Constructed(ss) => Some(ss.as_rooted()),
54 }
55 }
56
57 pub(crate) fn is_a_valid_owner(&self) -> bool {
58 match self {
59 StylesheetSource::Element(el) => el.as_stylesheet_owner().is_some(),
60 StylesheetSource::Constructed(ss) => ss.is_constructed(),
61 }
62 }
63
64 pub(crate) fn is_constructed(&self) -> bool {
65 matches!(self, StylesheetSource::Constructed(_))
66 }
67}
68
69#[derive(Clone, JSTraceable, MallocSizeOf)]
70#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
71pub(crate) struct ServoStylesheetInDocument {
72 #[ignore_malloc_size_of = "Stylo"]
73 #[no_trace]
74 pub(crate) sheet: Arc<Stylesheet>,
75 pub(crate) owner: StylesheetSource,
79}
80
81impl stylo_malloc_size_of::MallocSizeOf for ServoStylesheetInDocument {
84 fn size_of(&self, ops: &mut stylo_malloc_size_of::MallocSizeOfOps) -> usize {
85 <ServoStylesheetInDocument as malloc_size_of::MallocSizeOf>::size_of(self, ops)
86 }
87}
88
89impl fmt::Debug for ServoStylesheetInDocument {
90 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
91 self.sheet.fmt(formatter)
92 }
93}
94
95impl PartialEq for ServoStylesheetInDocument {
96 fn eq(&self, other: &Self) -> bool {
97 Arc::ptr_eq(&self.sheet, &other.sheet)
98 }
99}
100
101impl ::style::stylesheets::StylesheetInDocument for ServoStylesheetInDocument {
102 fn enabled(&self) -> bool {
103 self.sheet.enabled()
104 }
105
106 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
107 self.sheet.media(guard)
108 }
109
110 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
111 self.sheet.contents(guard)
112 }
113
114 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
115 None
116 }
117}
118
119#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
121#[derive(JSTraceable, MallocSizeOf)]
122pub(crate) struct DocumentOrShadowRoot {
123 window: Dom<Window>,
124 custom_element_registry: MutNullableDom<CustomElementRegistry>,
125}
126
127impl DocumentOrShadowRoot {
128 pub(crate) fn new(window: &Window) -> Self {
129 Self {
130 window: Dom::from_ref(window),
131 custom_element_registry: MutNullableDom::new(None),
132 }
133 }
134
135 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
137 self.custom_element_registry.get()
138 }
139
140 pub(crate) fn set_custom_element_registry(&self, registry: Option<&CustomElementRegistry>) {
141 self.custom_element_registry.set(registry);
142 }
143
144 pub(crate) fn retarget_hit_test_result(
147 &self,
148 this: &Node,
149 node: DomRoot<Node>,
150 ) -> Option<DomRoot<Element>> {
151 let retargeted_node =
152 DomRoot::downcast::<Node>(node.upcast::<EventTarget>().retarget(this.upcast()))?;
153 DomRoot::downcast::<Element>(retargeted_node.clone()).or_else(|| {
154 let parent_node = retargeted_node.GetParentNode()?;
155
156 if let Some(shadow_root) = parent_node.downcast::<ShadowRoot>() {
161 Some(shadow_root.Host())
162 } else {
163 retargeted_node.GetParentElement()
164 }
165 })
166 }
167
168 #[expect(unsafe_code)]
170 pub(crate) fn element_from_point(
171 &self,
172 this: &Node,
173 x: Finite<f64>,
174 y: Finite<f64>,
175 document_element: Option<DomRoot<Element>>,
176 has_browsing_context: bool,
177 ) -> Option<DomRoot<Element>> {
178 let x = *x as f32;
179 let y = *y as f32;
180 let viewport = self.window.viewport_details().size;
181
182 if !has_browsing_context {
183 return None;
184 }
185
186 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
187 return None;
188 }
189
190 let results = self
191 .window
192 .elements_from_point_query(LayoutPoint::new(x, y));
193 let Some(result) = results.first() else {
194 return document_element;
195 };
196
197 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
200 let node = unsafe { node::from_untrusted_node_address(address) };
201
202 self.retarget_hit_test_result(this, node)
203 }
204
205 #[expect(unsafe_code)]
207 pub(crate) fn elements_from_point(
208 &self,
209 this: &Node,
210 x: Finite<f64>,
211 y: Finite<f64>,
212 document_element: Option<DomRoot<Element>>,
213 has_browsing_context: bool,
214 ) -> Vec<DomRoot<Element>> {
215 let x = *x as f32;
216 let y = *y as f32;
217 let viewport = self.window.viewport_details().size;
218
219 if !has_browsing_context {
220 return vec![];
221 }
222
223 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
225 return vec![];
226 }
227
228 let nodes = self
234 .window
235 .elements_from_point_query(LayoutPoint::new(x, y));
236
237 let mut elements: Vec<_> = nodes
238 .iter()
239 .flat_map(|result| {
240 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
243 let node = unsafe { node::from_untrusted_node_address(address) };
244 self.retarget_hit_test_result(this, node)
245 })
246 .collect();
247
248 let mut last_seen = None;
251 elements.retain(|element| {
252 if Some(element) == last_seen.as_ref() {
253 return false;
254 }
255 last_seen = Some(element.clone());
256 true
257 });
258
259 if let Some(root_element) = document_element &&
262 elements.last() != Some(&root_element)
263 {
264 elements.push(root_element);
265 }
266
267 elements
269 }
270
271 pub(crate) fn active_element(&self, this: &Node) -> Option<DomRoot<Element>> {
273 let document = self.window.Document();
275 let candidate = document
276 .focus_handler()
277 .focused_area()
278 .dom_anchor(&document);
279
280 let candidate =
285 DomRoot::downcast::<Node>(candidate.upcast::<EventTarget>().retarget(this.upcast()))?;
286
287 if this != &*candidate.GetRootNode(&GetRootNodeOptions::empty()) {
289 return None;
290 }
291
292 if let Some(candidate) = DomRoot::downcast::<Element>(candidate.clone()) {
294 return Some(candidate);
295 }
296 assert!(candidate.is::<Document>());
297
298 if let Some(body) = document.GetBody() {
300 return Some(DomRoot::upcast(body));
301 }
302
303 if let Some(document_element) = document.GetDocumentElement() {
305 return Some(document_element);
306 }
307
308 None
310 }
311
312 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(
315 owner: StylesheetSource,
316 s: &Arc<Stylesheet>,
317 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
318 ) {
319 let guard = s.shared_lock.read();
320
321 stylesheets.remove_stylesheet(
323 None,
324 ServoStylesheetInDocument {
325 sheet: s.clone(),
326 owner,
327 },
328 &guard,
329 );
330 }
331
332 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_stylesheet(
336 owner: StylesheetSource,
337 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
338 sheet: Arc<Stylesheet>,
339 insertion_point: Option<ServoStylesheetInDocument>,
340 style_shared_lock: &StyleSharedRwLock,
341 ) {
342 debug_assert!(owner.is_a_valid_owner(), "Wat");
343
344 if owner.is_constructed() && !pref!(dom_adoptedstylesheet_enabled) {
345 return;
346 }
347
348 let sheet = ServoStylesheetInDocument { sheet, owner };
349
350 let guard = style_shared_lock.read();
351
352 match insertion_point {
353 Some(ip) => {
354 stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
355 },
356 None => {
357 stylesheets.append_stylesheet(None, sheet, &guard);
358 },
359 }
360 }
361
362 fn set_adopted_stylesheet(
375 cx: &mut JSContext,
376 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
377 incoming_stylesheets: &[Dom<CSSStyleSheet>],
378 owner: &StyleSheetListOwner,
379 ) -> ErrorResult {
380 if !pref!(dom_adoptedstylesheet_enabled) {
381 return Ok(());
382 }
383
384 let owner_doc = match owner {
385 StyleSheetListOwner::Document(doc) => doc,
386 StyleSheetListOwner::ShadowRoot(root) => root.owner_doc(),
387 };
388
389 for sheet in incoming_stylesheets.iter() {
390 if !sheet.constructor_document_matches(owner_doc) {
393 return Err(Error::NotAllowed(None));
394 }
395 }
396
397 let mut stylesheet_remove_set = HashSet::with_capacity(adopted_stylesheets.len());
399
400 for sheet_to_remove in adopted_stylesheets.iter() {
405 if stylesheet_remove_set.insert(sheet_to_remove) {
407 owner.remove_stylesheet(
408 StylesheetSource::Constructed(sheet_to_remove.clone()),
409 &sheet_to_remove.style_stylesheet(),
410 );
411 sheet_to_remove.remove_adopter(owner);
412 }
413 }
414
415 let mut stylesheet_add_set = HashSet::with_capacity(incoming_stylesheets.len());
417
418 for sheet in incoming_stylesheets.iter() {
421 if !stylesheet_add_set.insert(sheet) {
423 owner.remove_stylesheet(
427 StylesheetSource::Constructed(sheet.clone()),
428 &sheet.style_stylesheet(),
429 );
430 } else {
431 sheet.add_adopter(owner.clone());
432 }
433
434 owner.append_constructed_stylesheet(cx, sheet);
435 }
436
437 *adopted_stylesheets = incoming_stylesheets.to_vec();
438
439 Ok(())
440 }
441
442 pub(crate) fn set_adopted_stylesheet_from_jsval(
445 cx: &mut JSContext,
446 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
447 incoming_value: HandleValue,
448 owner: &StyleSheetListOwner,
449 ) -> ErrorResult {
450 let maybe_stylesheets =
451 Vec::<DomRoot<CSSStyleSheet>>::safe_from_jsval(cx, incoming_value, ());
452
453 match maybe_stylesheets {
454 Ok(ConversionResult::Success(stylesheets)) => {
455 rooted_vec!(let stylesheets <- stylesheets.iter().map(|s| s.as_traced()));
456
457 DocumentOrShadowRoot::set_adopted_stylesheet(
458 cx,
459 adopted_stylesheets,
460 &stylesheets,
461 owner,
462 )
463 },
464 Ok(ConversionResult::Failure(msg)) => Err(Error::Type(msg.into_owned())),
465 Err(_) => Err(Error::Type(
466 c"The provided value is not a sequence of 'CSSStylesheet'.".to_owned(),
467 )),
468 }
469 }
470}