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 layout_api::HitTestFlags;
14use script_bindings::cell::DomRefCell;
15use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
16use script_bindings::codegen::GenericBindings::ShadowRootBinding::ShadowRootMethods;
17use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
18use script_bindings::error::{Error, ErrorResult};
19use servo_arc::Arc;
20use servo_config::pref;
21use style::media_queries::MediaList;
22use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
23use style::stylesheets::scope_rule::ImplicitScopeRoot;
24use style::stylesheets::{Stylesheet, StylesheetContents};
25use webrender_api::units::LayoutPoint;
26
27use crate::dom::Document;
28use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
29use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
30use crate::dom::bindings::conversions::ConversionResult;
31use crate::dom::bindings::inheritance::Castable;
32use crate::dom::bindings::num::Finite;
33use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
34use crate::dom::css::stylesheetlist::StyleSheetListOwner;
35use crate::dom::customelementregistry::CustomElementRegistry;
36use crate::dom::element::Element;
37use crate::dom::node::{self, Node};
38use crate::dom::types::{CSSStyleSheet, EventTarget, ShadowRoot};
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, cx: &mut JSContext) -> Option<DomRoot<CSSStyleSheet>> {
53 match self {
54 StylesheetSource::Element(el) => el.upcast::<Node>().get_cssom_stylesheet(cx),
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(JSTraceable, MallocSizeOf)]
124pub(crate) struct DocumentOrShadowRoot {
125 window: Dom<Window>,
126 custom_element_registry: MutNullableDom<CustomElementRegistry>,
127}
128
129impl DocumentOrShadowRoot {
130 pub(crate) fn new(window: &Window) -> Self {
131 Self {
132 window: Dom::from_ref(window),
133 custom_element_registry: MutNullableDom::new(None),
134 }
135 }
136
137 pub(crate) fn custom_element_registry(&self) -> Option<DomRoot<CustomElementRegistry>> {
139 self.custom_element_registry.get()
140 }
141
142 pub(crate) fn set_custom_element_registry(&self, registry: Option<&CustomElementRegistry>) {
143 self.custom_element_registry.set(registry);
144 }
145
146 pub(crate) fn retarget_hit_test_result(
149 &self,
150 this: &Node,
151 node: &Node,
152 ) -> Option<DomRoot<Element>> {
153 let retargeted_node =
154 DomRoot::downcast::<Node>(node.upcast::<EventTarget>().retarget(this.upcast()))?;
155 DomRoot::downcast::<Element>(retargeted_node.clone()).or_else(|| {
156 let parent_node = retargeted_node.GetParentNode()?;
157
158 if let Some(shadow_root) = parent_node.downcast::<ShadowRoot>() {
163 Some(shadow_root.Host())
164 } else {
165 retargeted_node.GetParentElement()
166 }
167 })
168 }
169
170 #[expect(unsafe_code)]
172 pub(crate) fn element_from_point(
173 &self,
174 this: &Node,
175 x: Finite<f64>,
176 y: Finite<f64>,
177 document_element: Option<DomRoot<Element>>,
178 has_browsing_context: bool,
179 ) -> Option<DomRoot<Element>> {
180 let x = *x as f32;
181 let y = *y as f32;
182 let viewport = self.window.viewport_details().size;
183
184 if !has_browsing_context {
185 return None;
186 }
187
188 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
189 return None;
190 }
191
192 let flags = HitTestFlags::empty();
193 let result = self
194 .window
195 .elements_from_point_query(flags, LayoutPoint::new(x, y));
196 let Some(result) = result.items.first() else {
197 return document_element;
198 };
199
200 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
203 let node = unsafe { node::from_untrusted_node_address(address) };
204
205 self.retarget_hit_test_result(this, &node)
206 }
207
208 #[expect(unsafe_code)]
210 pub(crate) fn elements_from_point(
211 &self,
212 this: &Node,
213 x: Finite<f64>,
214 y: Finite<f64>,
215 document_element: Option<DomRoot<Element>>,
216 has_browsing_context: bool,
217 ) -> Vec<DomRoot<Element>> {
218 let x = *x as f32;
219 let y = *y as f32;
220 let viewport = self.window.viewport_details().size;
221
222 if !has_browsing_context {
223 return vec![];
224 }
225
226 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
228 return vec![];
229 }
230
231 let flags = HitTestFlags::empty();
237 let result = self
238 .window
239 .elements_from_point_query(flags, LayoutPoint::new(x, y));
240
241 let mut elements: Vec<_> = result
242 .items
243 .iter()
244 .flat_map(|result| {
245 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
248 let node = unsafe { node::from_untrusted_node_address(address) };
249 self.retarget_hit_test_result(this, &node)
250 })
251 .collect();
252
253 let mut last_seen = None;
256 elements.retain(|element| {
257 if Some(element) == last_seen.as_ref() {
258 return false;
259 }
260 last_seen = Some(element.clone());
261 true
262 });
263
264 if let Some(root_element) = document_element &&
267 elements.last() != Some(&root_element)
268 {
269 elements.push(root_element);
270 }
271
272 elements
274 }
275
276 pub(crate) fn active_element(&self, this: &Node) -> Option<DomRoot<Element>> {
278 let document = self.window.Document();
280 let candidate = document
281 .focus_handler()
282 .focused_area()
283 .dom_anchor(&document);
284
285 let candidate =
290 DomRoot::downcast::<Node>(candidate.upcast::<EventTarget>().retarget(this.upcast()))?;
291
292 if this != &*candidate.GetRootNode(&GetRootNodeOptions::empty()) {
294 return None;
295 }
296
297 if let Some(candidate) = DomRoot::downcast::<Element>(candidate.clone()) {
299 return Some(candidate);
300 }
301 assert!(candidate.is::<Document>());
302
303 if let Some(body) = document.GetBody() {
305 return Some(DomRoot::upcast(body));
306 }
307
308 if let Some(document_element) = document.GetDocumentElement() {
310 return Some(document_element);
311 }
312
313 None
315 }
316
317 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(
320 owner: StylesheetSource,
321 s: &Arc<Stylesheet>,
322 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
323 ) {
324 let guard = s.shared_lock.read();
325
326 stylesheets.remove_stylesheet(
328 None,
329 ServoStylesheetInDocument {
330 sheet: s.clone(),
331 owner,
332 },
333 &guard,
334 );
335 }
336
337 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_stylesheet(
341 owner: StylesheetSource,
342 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
343 sheet: Arc<Stylesheet>,
344 insertion_point: Option<ServoStylesheetInDocument>,
345 style_shared_lock: &StyleSharedRwLock,
346 ) {
347 debug_assert!(owner.is_a_valid_owner(), "Wat");
348
349 if owner.is_constructed() && !pref!(dom_adoptedstylesheet_enabled) {
350 return;
351 }
352
353 let sheet = ServoStylesheetInDocument { sheet, owner };
354
355 let guard = style_shared_lock.read();
356
357 match insertion_point {
358 Some(ip) => {
359 stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
360 },
361 None => {
362 stylesheets.append_stylesheet(None, sheet, &guard);
363 },
364 }
365 }
366
367 fn set_adopted_stylesheet(
380 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
381 incoming_stylesheets: &[Dom<CSSStyleSheet>],
382 owner: &StyleSheetListOwner,
383 ) -> ErrorResult {
384 if !pref!(dom_adoptedstylesheet_enabled) {
385 return Ok(());
386 }
387
388 let owner_doc = match owner {
389 StyleSheetListOwner::Document(doc) => doc,
390 StyleSheetListOwner::ShadowRoot(root) => root.owner_doc(),
391 };
392
393 for sheet in incoming_stylesheets.iter() {
394 if !sheet.constructor_document_matches(owner_doc) {
397 return Err(Error::NotAllowed(None));
398 }
399 }
400
401 let mut stylesheet_remove_set = HashSet::with_capacity(adopted_stylesheets.len());
403
404 for sheet_to_remove in adopted_stylesheets.iter() {
409 if stylesheet_remove_set.insert(sheet_to_remove) {
411 owner.remove_stylesheet(
412 StylesheetSource::Constructed(sheet_to_remove.clone()),
413 &sheet_to_remove.style_stylesheet(),
414 );
415 sheet_to_remove.remove_adopter(owner);
416 }
417 }
418
419 let mut stylesheet_add_set = HashSet::with_capacity(incoming_stylesheets.len());
421
422 for sheet in incoming_stylesheets.iter() {
425 if !stylesheet_add_set.insert(sheet) {
427 owner.remove_stylesheet(
431 StylesheetSource::Constructed(sheet.clone()),
432 &sheet.style_stylesheet(),
433 );
434 } else {
435 sheet.add_adopter(owner.clone());
436 }
437
438 owner.append_constructed_stylesheet(sheet);
439 }
440
441 *adopted_stylesheets = incoming_stylesheets.to_vec();
442
443 Ok(())
444 }
445
446 pub(crate) fn set_adopted_stylesheet_from_jsval(
449 cx: &mut JSContext,
450 adopted_stylesheets: &DomRefCell<Vec<Dom<CSSStyleSheet>>>,
451 incoming_value: HandleValue,
452 owner: &StyleSheetListOwner,
453 ) -> ErrorResult {
454 let maybe_stylesheets =
455 Vec::<DomRoot<CSSStyleSheet>>::safe_from_jsval(cx, incoming_value, ())
456 .map_err(|_| Error::JSFailed)?;
457
458 match maybe_stylesheets {
459 ConversionResult::Success(stylesheets) => {
460 rooted_vec!(let stylesheets <- stylesheets.iter().map(|s| s.as_traced()));
461
462 let mut sheets = adopted_stylesheets.safe_borrow_mut(cx);
463 DocumentOrShadowRoot::set_adopted_stylesheet(sheets.as_mut(), &stylesheets, owner)
464 },
465 ConversionResult::Failure(msg) => Err(Error::Type(msg.into_owned())),
466 }
467 }
468}