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::error::{Error, ErrorResult};
14use script_bindings::script_runtime::JSContext;
15use servo_arc::Arc;
16use servo_config::pref;
17use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
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 stylo_atoms::Atom;
23use webrender_api::units::LayoutPoint;
24
25use super::bindings::trace::HashMapTracedValues;
26use crate::dom::bindings::cell::DomRefCell;
27use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
28use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootMethods;
29use crate::dom::bindings::conversions::{ConversionResult, SafeFromJSValConvertible};
30use crate::dom::bindings::inheritance::Castable;
31use crate::dom::bindings::num::Finite;
32use crate::dom::bindings::root::{Dom, DomRoot};
33use crate::dom::element::Element;
34use crate::dom::html::htmlelement::HTMLElement;
35use crate::dom::node::{self, Node, VecPreOrderInsertionHelper};
36use crate::dom::shadowroot::ShadowRoot;
37use crate::dom::stylesheetlist::StyleSheetListOwner;
38use crate::dom::types::CSSStyleSheet;
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) -> Option<DomRoot<CSSStyleSheet>> {
53 match self {
54 StylesheetSource::Element(el) => el.upcast::<Node>().get_cssom_stylesheet(),
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 = "Arc"]
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 ToMediaListKey for ServoStylesheetInDocument {
104 fn to_media_list_key(&self) -> MediaListKey {
105 self.sheet.contents.to_media_list_key()
106 }
107}
108
109impl ::style::stylesheets::StylesheetInDocument for ServoStylesheetInDocument {
110 fn enabled(&self) -> bool {
111 self.sheet.enabled()
112 }
113
114 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
115 self.sheet.media(guard)
116 }
117
118 fn contents(&self) -> &StylesheetContents {
119 self.sheet.contents()
120 }
121
122 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
123 None
124 }
125}
126
127#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
129#[derive(Clone, JSTraceable, MallocSizeOf)]
130pub(crate) struct DocumentOrShadowRoot {
131 window: Dom<Window>,
132}
133
134impl DocumentOrShadowRoot {
135 pub(crate) fn new(window: &Window) -> Self {
136 Self {
137 window: Dom::from_ref(window),
138 }
139 }
140
141 #[allow(unsafe_code)]
142 pub(crate) fn element_from_point(
144 &self,
145 x: Finite<f64>,
146 y: Finite<f64>,
147 document_element: Option<DomRoot<Element>>,
148 has_browsing_context: bool,
149 ) -> Option<DomRoot<Element>> {
150 let x = *x as f32;
151 let y = *y as f32;
152 let viewport = self.window.viewport_details().size;
153
154 if !has_browsing_context {
155 return None;
156 }
157
158 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
159 return None;
160 }
161
162 match self
163 .window
164 .elements_from_point_query(LayoutPoint::new(x, y), ElementsFromPointFlags::empty())
165 .first()
166 {
167 Some(result) => {
168 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
171 let node = unsafe { node::from_untrusted_node_address(address) };
172 let parent_node = node.GetParentNode().unwrap();
173 let shadow_host = parent_node
174 .downcast::<ShadowRoot>()
175 .map(ShadowRootMethods::Host);
176 let element_ref = node
177 .downcast::<Element>()
178 .or(shadow_host.as_deref())
179 .unwrap_or_else(|| {
180 parent_node
181 .downcast::<Element>()
182 .expect("Hit node should have an element or shadowroot parent")
183 });
184
185 Some(DomRoot::from_ref(element_ref))
186 },
187 None => document_element,
188 }
189 }
190
191 #[allow(unsafe_code)]
192 pub(crate) fn elements_from_point(
194 &self,
195 x: Finite<f64>,
196 y: Finite<f64>,
197 document_element: Option<DomRoot<Element>>,
198 has_browsing_context: bool,
199 ) -> Vec<DomRoot<Element>> {
200 let x = *x as f32;
201 let y = *y as f32;
202 let viewport = self.window.viewport_details().size;
203
204 if !has_browsing_context {
205 return vec![];
206 }
207
208 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
210 return vec![];
211 }
212
213 let nodes = self
215 .window
216 .elements_from_point_query(LayoutPoint::new(x, y), ElementsFromPointFlags::FindAll);
217 let mut elements: Vec<DomRoot<Element>> = nodes
218 .iter()
219 .flat_map(|result| {
220 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
223 let node = unsafe { node::from_untrusted_node_address(address) };
224 DomRoot::downcast::<Element>(node)
225 })
226 .collect();
227
228 if let Some(root_element) = document_element {
230 if elements.last() != Some(&root_element) {
231 elements.push(root_element);
232 }
233 }
234
235 elements
237 }
238
239 pub(crate) fn get_active_element(
241 &self,
242 focused_element: Option<DomRoot<Element>>,
243 body: Option<DomRoot<HTMLElement>>,
244 document_element: Option<DomRoot<Element>>,
245 ) -> Option<DomRoot<Element>> {
246 match focused_element {
249 Some(element) => Some(element), None => match body {
251 Some(body) => Some(DomRoot::upcast(body)),
253 None => document_element,
254 },
255 }
256 }
257
258 #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(
261 owner: StylesheetSource,
262 s: &Arc<Stylesheet>,
263 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
264 ) {
265 let guard = s.shared_lock.read();
266
267 stylesheets.remove_stylesheet(
269 None,
270 ServoStylesheetInDocument {
271 sheet: s.clone(),
272 owner,
273 },
274 &guard,
275 );
276 }
277
278 #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn add_stylesheet(
282 owner: StylesheetSource,
283 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
284 sheet: Arc<Stylesheet>,
285 insertion_point: Option<ServoStylesheetInDocument>,
286 style_shared_lock: &StyleSharedRwLock,
287 ) {
288 debug_assert!(owner.is_a_valid_owner(), "Wat");
289
290 if owner.is_constructed() && !pref!(dom_adoptedstylesheet_enabled) {
291 return;
292 }
293
294 let sheet = ServoStylesheetInDocument { sheet, owner };
295
296 let guard = style_shared_lock.read();
297
298 match insertion_point {
299 Some(ip) => {
300 stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
301 },
302 None => {
303 stylesheets.append_stylesheet(None, sheet, &guard);
304 },
305 }
306 }
307
308 pub(crate) fn unregister_named_element(
310 &self,
311 id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
312 to_unregister: &Element,
313 id: &Atom,
314 ) {
315 debug!(
316 "Removing named element {:p}: {:p} id={}",
317 self, to_unregister, id
318 );
319 let mut id_map = id_map.borrow_mut();
320 let is_empty = match id_map.get_mut(id) {
321 None => false,
322 Some(elements) => {
323 let position = elements
324 .iter()
325 .position(|element| &**element == to_unregister)
326 .expect("This element should be in registered.");
327 elements.remove(position);
328 elements.is_empty()
329 },
330 };
331 if is_empty {
332 id_map.remove(id);
333 }
334 }
335
336 pub(crate) fn register_named_element(
338 &self,
339 id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
340 element: &Element,
341 id: &Atom,
342 root: DomRoot<Node>,
343 ) {
344 debug!("Adding named element {:p}: {:p} id={}", self, element, id);
345 assert!(
346 element.upcast::<Node>().is_in_a_document_tree() ||
347 element.upcast::<Node>().is_in_a_shadow_tree()
348 );
349 assert!(!id.is_empty());
350 let mut id_map = id_map.borrow_mut();
351 let elements = id_map.entry(id.clone()).or_default();
352 elements.insert_pre_order(element, &root);
353 }
354
355 fn set_adopted_stylesheet(
368 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
369 incoming_stylesheets: &[Dom<CSSStyleSheet>],
370 owner: &StyleSheetListOwner,
371 ) -> ErrorResult {
372 if !pref!(dom_adoptedstylesheet_enabled) {
373 return Ok(());
374 }
375
376 let owner_doc = match owner {
377 StyleSheetListOwner::Document(doc) => doc,
378 StyleSheetListOwner::ShadowRoot(root) => root.owner_doc(),
379 };
380
381 for sheet in incoming_stylesheets.iter() {
382 if !sheet.constructor_document_matches(owner_doc) {
385 return Err(Error::NotAllowed);
386 }
387 }
388
389 let mut stylesheet_remove_set = HashSet::with_capacity(adopted_stylesheets.len());
391
392 for sheet_to_remove in adopted_stylesheets.iter() {
397 if stylesheet_remove_set.insert(sheet_to_remove) {
399 owner.remove_stylesheet(
400 StylesheetSource::Constructed(sheet_to_remove.clone()),
401 &sheet_to_remove.style_stylesheet(),
402 );
403 sheet_to_remove.remove_adopter(owner);
404 }
405 }
406
407 let mut stylesheet_add_set = HashSet::with_capacity(incoming_stylesheets.len());
409
410 for sheet in incoming_stylesheets.iter() {
413 if !stylesheet_add_set.insert(sheet) {
415 owner.remove_stylesheet(
419 StylesheetSource::Constructed(sheet.clone()),
420 &sheet.style_stylesheet(),
421 );
422 } else {
423 sheet.add_adopter(owner.clone());
424 }
425
426 owner.append_constructed_stylesheet(sheet);
427 }
428
429 *adopted_stylesheets = incoming_stylesheets.to_vec();
430
431 Ok(())
432 }
433
434 pub(crate) fn set_adopted_stylesheet_from_jsval(
437 context: JSContext,
438 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
439 incoming_value: HandleValue,
440 owner: &StyleSheetListOwner,
441 ) -> ErrorResult {
442 let maybe_stylesheets =
443 Vec::<DomRoot<CSSStyleSheet>>::safe_from_jsval(context, incoming_value, ());
444
445 match maybe_stylesheets {
446 Ok(ConversionResult::Success(stylesheets)) => {
447 rooted_vec!(let stylesheets <- stylesheets.to_owned().iter().map(|s| s.as_traced()));
448
449 DocumentOrShadowRoot::set_adopted_stylesheet(
450 adopted_stylesheets,
451 &stylesheets,
452 owner,
453 )
454 },
455 Ok(ConversionResult::Failure(msg)) => Err(Error::Type(msg.to_string())),
456 Err(_) => Err(Error::Type(
457 "The provided value is not a sequence of 'CSSStylesheet'.".to_owned(),
458 )),
459 }
460 }
461}