1use std::collections::HashSet;
6use std::ffi::c_void;
7use std::fmt;
8
9use embedder_traits::UntrustedNodeAddress;
10use js::context::JSContext;
11use js::rust::HandleValue;
12use script_bindings::codegen::GenericBindings::DocumentBinding::DocumentMethods;
13use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
14use script_bindings::error::{Error, ErrorResult};
15use script_bindings::script_runtime::CanGc;
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::dom::Document;
25use crate::dom::bindings::codegen::Bindings::NodeBinding::GetRootNodeOptions;
26use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
27use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMethods;
28use crate::dom::bindings::conversions::{ConversionResult, SafeFromJSValConvertible};
29use crate::dom::bindings::inheritance::Castable;
30use crate::dom::bindings::num::Finite;
31use crate::dom::bindings::root::{Dom, DomRoot};
32use crate::dom::css::stylesheetlist::StyleSheetListOwner;
33use crate::dom::element::Element;
34use crate::dom::node::{self, Node};
35use crate::dom::shadowroot::ShadowRoot;
36use crate::dom::types::{CSSStyleSheet, EventTarget};
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(Clone, JSTraceable, MallocSizeOf)]
122pub(crate) struct DocumentOrShadowRoot {
123 window: Dom<Window>,
124}
125
126impl DocumentOrShadowRoot {
127 pub(crate) fn new(window: &Window) -> Self {
128 Self {
129 window: Dom::from_ref(window),
130 }
131 }
132
133 #[expect(unsafe_code)]
134 pub(crate) fn element_from_point(
136 &self,
137 x: Finite<f64>,
138 y: Finite<f64>,
139 document_element: Option<DomRoot<Element>>,
140 has_browsing_context: bool,
141 ) -> Option<DomRoot<Element>> {
142 let x = *x as f32;
143 let y = *y as f32;
144 let viewport = self.window.viewport_details().size;
145
146 if !has_browsing_context {
147 return None;
148 }
149
150 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
151 return None;
152 }
153
154 let results = self
155 .window
156 .elements_from_point_query(LayoutPoint::new(x, y));
157 let Some(result) = results.first() else {
158 return document_element;
159 };
160
161 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
164 let node = unsafe { node::from_untrusted_node_address(address) };
165 DomRoot::downcast::<Element>(node.clone()).or_else(|| {
166 let parent_node = node.GetParentNode()?;
167 if let Some(shadow_root) = parent_node.downcast::<ShadowRoot>() {
168 Some(shadow_root.Host())
169 } else {
170 node.GetParentElement()
171 }
172 })
173 }
174
175 #[expect(unsafe_code)]
176 pub(crate) fn elements_from_point(
178 &self,
179 x: Finite<f64>,
180 y: Finite<f64>,
181 document_element: Option<DomRoot<Element>>,
182 has_browsing_context: bool,
183 ) -> Vec<DomRoot<Element>> {
184 let x = *x as f32;
185 let y = *y as f32;
186 let viewport = self.window.viewport_details().size;
187
188 if !has_browsing_context {
189 return vec![];
190 }
191
192 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
194 return vec![];
195 }
196
197 let nodes = self
199 .window
200 .elements_from_point_query(LayoutPoint::new(x, y));
201 let mut elements: Vec<DomRoot<Element>> = nodes
202 .iter()
203 .flat_map(|result| {
204 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
207 let node = unsafe { node::from_untrusted_node_address(address) };
208 DomRoot::downcast::<Element>(node)
209 })
210 .collect();
211
212 if let Some(root_element) = document_element &&
214 elements.last() != Some(&root_element)
215 {
216 elements.push(root_element);
217 }
218
219 elements
221 }
222
223 pub(crate) fn active_element(&self, this: &Node) -> Option<DomRoot<Element>> {
225 let document = self.window.Document();
227 let candidate = document
228 .focus_handler()
229 .focused_area()
230 .dom_anchor(&document);
231
232 let candidate =
237 DomRoot::downcast::<Node>(candidate.upcast::<EventTarget>().retarget(this.upcast()))?;
238
239 if this != &*candidate.GetRootNode(&GetRootNodeOptions::empty()) {
241 return None;
242 }
243
244 if let Some(candidate) = DomRoot::downcast::<Element>(candidate.clone()) {
246 return Some(candidate);
247 }
248 assert!(candidate.is::<Document>());
249
250 if let Some(body) = document.GetBody() {
252 return Some(DomRoot::upcast(body));
253 }
254
255 if let Some(document_element) = document.GetDocumentElement() {
257 return Some(document_element);
258 }
259
260 None
262 }
263
264 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(
267 owner: StylesheetSource,
268 s: &Arc<Stylesheet>,
269 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
270 ) {
271 let guard = s.shared_lock.read();
272
273 stylesheets.remove_stylesheet(
275 None,
276 ServoStylesheetInDocument {
277 sheet: s.clone(),
278 owner,
279 },
280 &guard,
281 );
282 }
283
284 #[cfg_attr(crown, expect(crown::unrooted_must_root))] pub(crate) fn add_stylesheet(
288 owner: StylesheetSource,
289 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
290 sheet: Arc<Stylesheet>,
291 insertion_point: Option<ServoStylesheetInDocument>,
292 style_shared_lock: &StyleSharedRwLock,
293 ) {
294 debug_assert!(owner.is_a_valid_owner(), "Wat");
295
296 if owner.is_constructed() && !pref!(dom_adoptedstylesheet_enabled) {
297 return;
298 }
299
300 let sheet = ServoStylesheetInDocument { sheet, owner };
301
302 let guard = style_shared_lock.read();
303
304 match insertion_point {
305 Some(ip) => {
306 stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
307 },
308 None => {
309 stylesheets.append_stylesheet(None, sheet, &guard);
310 },
311 }
312 }
313
314 fn set_adopted_stylesheet(
327 cx: &mut JSContext,
328 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
329 incoming_stylesheets: &[Dom<CSSStyleSheet>],
330 owner: &StyleSheetListOwner,
331 ) -> ErrorResult {
332 if !pref!(dom_adoptedstylesheet_enabled) {
333 return Ok(());
334 }
335
336 let owner_doc = match owner {
337 StyleSheetListOwner::Document(doc) => doc,
338 StyleSheetListOwner::ShadowRoot(root) => root.owner_doc(),
339 };
340
341 for sheet in incoming_stylesheets.iter() {
342 if !sheet.constructor_document_matches(owner_doc) {
345 return Err(Error::NotAllowed(None));
346 }
347 }
348
349 let mut stylesheet_remove_set = HashSet::with_capacity(adopted_stylesheets.len());
351
352 for sheet_to_remove in adopted_stylesheets.iter() {
357 if stylesheet_remove_set.insert(sheet_to_remove) {
359 owner.remove_stylesheet(
360 StylesheetSource::Constructed(sheet_to_remove.clone()),
361 &sheet_to_remove.style_stylesheet(),
362 );
363 sheet_to_remove.remove_adopter(owner);
364 }
365 }
366
367 let mut stylesheet_add_set = HashSet::with_capacity(incoming_stylesheets.len());
369
370 for sheet in incoming_stylesheets.iter() {
373 if !stylesheet_add_set.insert(sheet) {
375 owner.remove_stylesheet(
379 StylesheetSource::Constructed(sheet.clone()),
380 &sheet.style_stylesheet(),
381 );
382 } else {
383 sheet.add_adopter(owner.clone());
384 }
385
386 owner.append_constructed_stylesheet(cx, sheet);
387 }
388
389 *adopted_stylesheets = incoming_stylesheets.to_vec();
390
391 Ok(())
392 }
393
394 pub(crate) fn set_adopted_stylesheet_from_jsval(
397 cx: &mut JSContext,
398 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
399 incoming_value: HandleValue,
400 owner: &StyleSheetListOwner,
401 ) -> ErrorResult {
402 let maybe_stylesheets = Vec::<DomRoot<CSSStyleSheet>>::safe_from_jsval(
403 cx.into(),
404 incoming_value,
405 (),
406 CanGc::from_cx(cx),
407 );
408
409 match maybe_stylesheets {
410 Ok(ConversionResult::Success(stylesheets)) => {
411 rooted_vec!(let stylesheets <- stylesheets.iter().map(|s| s.as_traced()));
412
413 DocumentOrShadowRoot::set_adopted_stylesheet(
414 cx,
415 adopted_stylesheets,
416 &stylesheets,
417 owner,
418 )
419 },
420 Ok(ConversionResult::Failure(msg)) => Err(Error::Type(msg.into_owned())),
421 Err(_) => Err(Error::Type(
422 c"The provided value is not a sequence of 'CSSStylesheet'.".to_owned(),
423 )),
424 }
425 }
426}