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 script_bindings::error::{Error, ErrorResult};
13use script_bindings::script_runtime::JSContext;
14use servo_arc::Arc;
15use servo_config::pref;
16use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
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::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::element::Element;
33use crate::dom::html::htmlelement::HTMLElement;
34use crate::dom::node::{self, Node, VecPreOrderInsertionHelper};
35use crate::dom::shadowroot::ShadowRoot;
36use crate::dom::stylesheetlist::StyleSheetListOwner;
37use crate::dom::types::CSSStyleSheet;
38use crate::dom::window::Window;
39use crate::stylesheet_set::StylesheetSetRef;
40
41#[derive(Clone, JSTraceable, MallocSizeOf)]
44#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
45pub(crate) enum StylesheetSource {
46 Element(Dom<Element>),
47 Constructed(Dom<CSSStyleSheet>),
48}
49
50impl StylesheetSource {
51 pub(crate) fn get_cssom_object(&self) -> Option<DomRoot<CSSStyleSheet>> {
52 match self {
53 StylesheetSource::Element(el) => el.upcast::<Node>().get_cssom_stylesheet(),
54 StylesheetSource::Constructed(ss) => Some(ss.as_rooted()),
55 }
56 }
57
58 pub(crate) fn is_a_valid_owner(&self) -> bool {
59 match self {
60 StylesheetSource::Element(el) => el.as_stylesheet_owner().is_some(),
61 StylesheetSource::Constructed(ss) => ss.is_constructed(),
62 }
63 }
64
65 pub(crate) fn is_constructed(&self) -> bool {
66 matches!(self, StylesheetSource::Constructed(_))
67 }
68}
69
70#[derive(Clone, JSTraceable, MallocSizeOf)]
71#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
72pub(crate) struct ServoStylesheetInDocument {
73 #[ignore_malloc_size_of = "Arc"]
74 #[no_trace]
75 pub(crate) sheet: Arc<Stylesheet>,
76 pub(crate) owner: StylesheetSource,
80}
81
82impl stylo_malloc_size_of::MallocSizeOf for ServoStylesheetInDocument {
85 fn size_of(&self, ops: &mut stylo_malloc_size_of::MallocSizeOfOps) -> usize {
86 <ServoStylesheetInDocument as malloc_size_of::MallocSizeOf>::size_of(self, ops)
87 }
88}
89
90impl fmt::Debug for ServoStylesheetInDocument {
91 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
92 self.sheet.fmt(formatter)
93 }
94}
95
96impl PartialEq for ServoStylesheetInDocument {
97 fn eq(&self, other: &Self) -> bool {
98 Arc::ptr_eq(&self.sheet, &other.sheet)
99 }
100}
101
102impl ToMediaListKey for ServoStylesheetInDocument {
103 fn to_media_list_key(&self) -> MediaListKey {
104 self.sheet.contents.to_media_list_key()
105 }
106}
107
108impl ::style::stylesheets::StylesheetInDocument for ServoStylesheetInDocument {
109 fn enabled(&self) -> bool {
110 self.sheet.enabled()
111 }
112
113 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
114 self.sheet.media(guard)
115 }
116
117 fn contents(&self) -> &StylesheetContents {
118 self.sheet.contents()
119 }
120
121 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
122 None
123 }
124}
125
126#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
128#[derive(Clone, JSTraceable, MallocSizeOf)]
129pub(crate) struct DocumentOrShadowRoot {
130 window: Dom<Window>,
131}
132
133impl DocumentOrShadowRoot {
134 pub(crate) fn new(window: &Window) -> Self {
135 Self {
136 window: Dom::from_ref(window),
137 }
138 }
139
140 #[allow(unsafe_code)]
141 pub(crate) fn element_from_point(
143 &self,
144 x: Finite<f64>,
145 y: Finite<f64>,
146 document_element: Option<DomRoot<Element>>,
147 has_browsing_context: bool,
148 ) -> Option<DomRoot<Element>> {
149 let x = *x as f32;
150 let y = *y as f32;
151 let viewport = self.window.viewport_details().size;
152
153 if !has_browsing_context {
154 return None;
155 }
156
157 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
158 return None;
159 }
160
161 match self
162 .window
163 .elements_from_point_query(LayoutPoint::new(x, y), ElementsFromPointFlags::empty())
164 .first()
165 {
166 Some(result) => {
167 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
170 let node = unsafe { node::from_untrusted_node_address(address) };
171 let parent_node = node.GetParentNode().unwrap();
172 let shadow_host = parent_node
173 .downcast::<ShadowRoot>()
174 .map(ShadowRootMethods::Host);
175 let element_ref = node
176 .downcast::<Element>()
177 .or(shadow_host.as_deref())
178 .unwrap_or_else(|| {
179 parent_node
180 .downcast::<Element>()
181 .expect("Hit node should have an element or shadowroot parent")
182 });
183
184 Some(DomRoot::from_ref(element_ref))
185 },
186 None => document_element,
187 }
188 }
189
190 #[allow(unsafe_code)]
191 pub(crate) fn elements_from_point(
193 &self,
194 x: Finite<f64>,
195 y: Finite<f64>,
196 document_element: Option<DomRoot<Element>>,
197 has_browsing_context: bool,
198 ) -> Vec<DomRoot<Element>> {
199 let x = *x as f32;
200 let y = *y as f32;
201 let viewport = self.window.viewport_details().size;
202
203 if !has_browsing_context {
204 return vec![];
205 }
206
207 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
209 return vec![];
210 }
211
212 let nodes = self
214 .window
215 .elements_from_point_query(LayoutPoint::new(x, y), ElementsFromPointFlags::FindAll);
216 let mut elements: Vec<DomRoot<Element>> = nodes
217 .iter()
218 .flat_map(|result| {
219 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
222 let node = unsafe { node::from_untrusted_node_address(address) };
223 DomRoot::downcast::<Element>(node)
224 })
225 .collect();
226
227 if let Some(root_element) = document_element {
229 if elements.last() != Some(&root_element) {
230 elements.push(root_element);
231 }
232 }
233
234 elements
236 }
237
238 pub(crate) fn get_active_element(
240 &self,
241 focused_element: Option<DomRoot<Element>>,
242 body: Option<DomRoot<HTMLElement>>,
243 document_element: Option<DomRoot<Element>>,
244 ) -> Option<DomRoot<Element>> {
245 match focused_element {
248 Some(element) => Some(element), None => match body {
250 Some(body) => Some(DomRoot::upcast(body)),
252 None => document_element,
253 },
254 }
255 }
256
257 #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(
260 owner: StylesheetSource,
261 s: &Arc<Stylesheet>,
262 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
263 ) {
264 let guard = s.shared_lock.read();
265
266 stylesheets.remove_stylesheet(
268 None,
269 ServoStylesheetInDocument {
270 sheet: s.clone(),
271 owner,
272 },
273 &guard,
274 );
275 }
276
277 #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn add_stylesheet(
281 owner: StylesheetSource,
282 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
283 sheet: Arc<Stylesheet>,
284 insertion_point: Option<ServoStylesheetInDocument>,
285 style_shared_lock: &StyleSharedRwLock,
286 ) {
287 debug_assert!(owner.is_a_valid_owner(), "Wat");
288
289 if owner.is_constructed() && !pref!(dom_adoptedstylesheet_enabled) {
290 return;
291 }
292
293 let sheet = ServoStylesheetInDocument { sheet, owner };
294
295 let guard = style_shared_lock.read();
296
297 match insertion_point {
298 Some(ip) => {
299 stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
300 },
301 None => {
302 stylesheets.append_stylesheet(None, sheet, &guard);
303 },
304 }
305 }
306
307 pub(crate) fn unregister_named_element(
309 &self,
310 id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>>>,
311 to_unregister: &Element,
312 id: &Atom,
313 ) {
314 debug!(
315 "Removing named element {:p}: {:p} id={}",
316 self, to_unregister, id
317 );
318 let mut id_map = id_map.borrow_mut();
319 let is_empty = match id_map.get_mut(id) {
320 None => false,
321 Some(elements) => {
322 let position = elements
323 .iter()
324 .position(|element| &**element == to_unregister)
325 .expect("This element should be in registered.");
326 elements.remove(position);
327 elements.is_empty()
328 },
329 };
330 if is_empty {
331 id_map.remove(id);
332 }
333 }
334
335 pub(crate) fn register_named_element(
337 &self,
338 id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>>>,
339 element: &Element,
340 id: &Atom,
341 root: DomRoot<Node>,
342 ) {
343 debug!("Adding named element {:p}: {:p} id={}", self, element, id);
344 assert!(
345 element.upcast::<Node>().is_in_a_document_tree() ||
346 element.upcast::<Node>().is_in_a_shadow_tree()
347 );
348 assert!(!id.is_empty());
349 let mut id_map = id_map.borrow_mut();
350 let elements = id_map.entry(id.clone()).or_default();
351 elements.insert_pre_order(element, &root);
352 }
353
354 fn set_adopted_stylesheet(
367 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
368 incoming_stylesheets: &[Dom<CSSStyleSheet>],
369 owner: &StyleSheetListOwner,
370 ) -> ErrorResult {
371 if !pref!(dom_adoptedstylesheet_enabled) {
372 return Ok(());
373 }
374
375 let owner_doc = match owner {
376 StyleSheetListOwner::Document(doc) => doc,
377 StyleSheetListOwner::ShadowRoot(root) => root.owner_doc(),
378 };
379
380 for sheet in incoming_stylesheets.iter() {
381 if !sheet.constructor_document_matches(owner_doc) {
384 return Err(Error::NotAllowed);
385 }
386 }
387
388 let mut stylesheet_remove_set = HashSet::with_capacity(adopted_stylesheets.len());
390
391 for sheet_to_remove in adopted_stylesheets.iter() {
396 if stylesheet_remove_set.insert(sheet_to_remove) {
398 owner.remove_stylesheet(
399 StylesheetSource::Constructed(sheet_to_remove.clone()),
400 &sheet_to_remove.style_stylesheet(),
401 );
402 sheet_to_remove.remove_adopter(owner);
403 }
404 }
405
406 let mut stylesheet_add_set = HashSet::with_capacity(incoming_stylesheets.len());
408
409 for sheet in incoming_stylesheets.iter() {
412 if !stylesheet_add_set.insert(sheet) {
414 owner.remove_stylesheet(
418 StylesheetSource::Constructed(sheet.clone()),
419 &sheet.style_stylesheet(),
420 );
421 } else {
422 sheet.add_adopter(owner.clone());
423 }
424
425 owner.append_constructed_stylesheet(sheet);
426 }
427
428 *adopted_stylesheets = incoming_stylesheets.to_vec();
429
430 Ok(())
431 }
432
433 pub(crate) fn set_adopted_stylesheet_from_jsval(
436 context: JSContext,
437 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
438 incoming_value: HandleValue,
439 owner: &StyleSheetListOwner,
440 ) -> ErrorResult {
441 let maybe_stylesheets =
442 Vec::<DomRoot<CSSStyleSheet>>::safe_from_jsval(context, incoming_value, ());
443
444 match maybe_stylesheets {
445 Ok(ConversionResult::Success(stylesheets)) => {
446 rooted_vec!(let stylesheets <- stylesheets.to_owned().iter().map(|s| s.as_traced()));
447
448 DocumentOrShadowRoot::set_adopted_stylesheet(
449 adopted_stylesheets,
450 &stylesheets,
451 owner,
452 )
453 },
454 Ok(ConversionResult::Failure(msg)) => Err(Error::Type(msg.to_string())),
455 Err(_) => Err(Error::Type(
456 "The provided value is not a sequence of 'CSSStylesheet'.".to_owned(),
457 )),
458 }
459 }
460}