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::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::css::stylesheetlist::StyleSheetListOwner;
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::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 = "Stylo"]
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 ::style::stylesheets::StylesheetInDocument for ServoStylesheetInDocument {
103 fn enabled(&self) -> bool {
104 self.sheet.enabled()
105 }
106
107 fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
108 self.sheet.media(guard)
109 }
110
111 fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {
112 self.sheet.contents(guard)
113 }
114
115 fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
116 None
117 }
118}
119
120#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
122#[derive(Clone, JSTraceable, MallocSizeOf)]
123pub(crate) struct DocumentOrShadowRoot {
124 window: Dom<Window>,
125}
126
127impl DocumentOrShadowRoot {
128 pub(crate) fn new(window: &Window) -> Self {
129 Self {
130 window: Dom::from_ref(window),
131 }
132 }
133
134 #[allow(unsafe_code)]
135 pub(crate) fn element_from_point(
137 &self,
138 x: Finite<f64>,
139 y: Finite<f64>,
140 document_element: Option<DomRoot<Element>>,
141 has_browsing_context: bool,
142 ) -> Option<DomRoot<Element>> {
143 let x = *x as f32;
144 let y = *y as f32;
145 let viewport = self.window.viewport_details().size;
146
147 if !has_browsing_context {
148 return None;
149 }
150
151 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
152 return None;
153 }
154
155 match self
156 .window
157 .elements_from_point_query(LayoutPoint::new(x, y), ElementsFromPointFlags::empty())
158 .first()
159 {
160 Some(result) => {
161 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
164 let node = unsafe { node::from_untrusted_node_address(address) };
165 let parent_node = node.GetParentNode().unwrap();
166 let shadow_host = parent_node
167 .downcast::<ShadowRoot>()
168 .map(ShadowRootMethods::Host);
169 let element_ref = node
170 .downcast::<Element>()
171 .or(shadow_host.as_deref())
172 .unwrap_or_else(|| {
173 parent_node
174 .downcast::<Element>()
175 .expect("Hit node should have an element or shadowroot parent")
176 });
177
178 Some(DomRoot::from_ref(element_ref))
179 },
180 None => document_element,
181 }
182 }
183
184 #[allow(unsafe_code)]
185 pub(crate) fn elements_from_point(
187 &self,
188 x: Finite<f64>,
189 y: Finite<f64>,
190 document_element: Option<DomRoot<Element>>,
191 has_browsing_context: bool,
192 ) -> Vec<DomRoot<Element>> {
193 let x = *x as f32;
194 let y = *y as f32;
195 let viewport = self.window.viewport_details().size;
196
197 if !has_browsing_context {
198 return vec![];
199 }
200
201 if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
203 return vec![];
204 }
205
206 let nodes = self
208 .window
209 .elements_from_point_query(LayoutPoint::new(x, y), ElementsFromPointFlags::FindAll);
210 let mut elements: Vec<DomRoot<Element>> = nodes
211 .iter()
212 .flat_map(|result| {
213 let address = UntrustedNodeAddress(result.node.0 as *const c_void);
216 let node = unsafe { node::from_untrusted_node_address(address) };
217 DomRoot::downcast::<Element>(node)
218 })
219 .collect();
220
221 if let Some(root_element) = document_element {
223 if elements.last() != Some(&root_element) {
224 elements.push(root_element);
225 }
226 }
227
228 elements
230 }
231
232 pub(crate) fn get_active_element(
234 &self,
235 focused_element: Option<DomRoot<Element>>,
236 body: Option<DomRoot<HTMLElement>>,
237 document_element: Option<DomRoot<Element>>,
238 ) -> Option<DomRoot<Element>> {
239 match focused_element {
242 Some(element) => Some(element), None => match body {
244 Some(body) => Some(DomRoot::upcast(body)),
246 None => document_element,
247 },
248 }
249 }
250
251 #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn remove_stylesheet(
254 owner: StylesheetSource,
255 s: &Arc<Stylesheet>,
256 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
257 ) {
258 let guard = s.shared_lock.read();
259
260 stylesheets.remove_stylesheet(
262 None,
263 ServoStylesheetInDocument {
264 sheet: s.clone(),
265 owner,
266 },
267 &guard,
268 );
269 }
270
271 #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn add_stylesheet(
275 owner: StylesheetSource,
276 mut stylesheets: StylesheetSetRef<ServoStylesheetInDocument>,
277 sheet: Arc<Stylesheet>,
278 insertion_point: Option<ServoStylesheetInDocument>,
279 style_shared_lock: &StyleSharedRwLock,
280 ) {
281 debug_assert!(owner.is_a_valid_owner(), "Wat");
282
283 if owner.is_constructed() && !pref!(dom_adoptedstylesheet_enabled) {
284 return;
285 }
286
287 let sheet = ServoStylesheetInDocument { sheet, owner };
288
289 let guard = style_shared_lock.read();
290
291 match insertion_point {
292 Some(ip) => {
293 stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
294 },
295 None => {
296 stylesheets.append_stylesheet(None, sheet, &guard);
297 },
298 }
299 }
300
301 pub(crate) fn unregister_named_element(
303 &self,
304 id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
305 to_unregister: &Element,
306 id: &Atom,
307 ) {
308 debug!(
309 "Removing named element {:p}: {:p} id={}",
310 self, to_unregister, id
311 );
312 let mut id_map = id_map.borrow_mut();
313 let is_empty = match id_map.get_mut(id) {
314 None => false,
315 Some(elements) => {
316 let position = elements
317 .iter()
318 .position(|element| &**element == to_unregister)
319 .expect("This element should be in registered.");
320 elements.remove(position);
321 elements.is_empty()
322 },
323 };
324 if is_empty {
325 id_map.remove(id);
326 }
327 }
328
329 pub(crate) fn register_named_element(
331 &self,
332 id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>, FxBuildHasher>>,
333 element: &Element,
334 id: &Atom,
335 root: DomRoot<Node>,
336 ) {
337 debug!("Adding named element {:p}: {:p} id={}", self, element, id);
338 assert!(
339 element.upcast::<Node>().is_in_a_document_tree() ||
340 element.upcast::<Node>().is_in_a_shadow_tree()
341 );
342 assert!(!id.is_empty());
343 let mut id_map = id_map.borrow_mut();
344 let elements = id_map.entry(id.clone()).or_default();
345 elements.insert_pre_order(element, &root);
346 }
347
348 fn set_adopted_stylesheet(
361 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
362 incoming_stylesheets: &[Dom<CSSStyleSheet>],
363 owner: &StyleSheetListOwner,
364 ) -> ErrorResult {
365 if !pref!(dom_adoptedstylesheet_enabled) {
366 return Ok(());
367 }
368
369 let owner_doc = match owner {
370 StyleSheetListOwner::Document(doc) => doc,
371 StyleSheetListOwner::ShadowRoot(root) => root.owner_doc(),
372 };
373
374 for sheet in incoming_stylesheets.iter() {
375 if !sheet.constructor_document_matches(owner_doc) {
378 return Err(Error::NotAllowed);
379 }
380 }
381
382 let mut stylesheet_remove_set = HashSet::with_capacity(adopted_stylesheets.len());
384
385 for sheet_to_remove in adopted_stylesheets.iter() {
390 if stylesheet_remove_set.insert(sheet_to_remove) {
392 owner.remove_stylesheet(
393 StylesheetSource::Constructed(sheet_to_remove.clone()),
394 &sheet_to_remove.style_stylesheet(),
395 );
396 sheet_to_remove.remove_adopter(owner);
397 }
398 }
399
400 let mut stylesheet_add_set = HashSet::with_capacity(incoming_stylesheets.len());
402
403 for sheet in incoming_stylesheets.iter() {
406 if !stylesheet_add_set.insert(sheet) {
408 owner.remove_stylesheet(
412 StylesheetSource::Constructed(sheet.clone()),
413 &sheet.style_stylesheet(),
414 );
415 } else {
416 sheet.add_adopter(owner.clone());
417 }
418
419 owner.append_constructed_stylesheet(sheet);
420 }
421
422 *adopted_stylesheets = incoming_stylesheets.to_vec();
423
424 Ok(())
425 }
426
427 pub(crate) fn set_adopted_stylesheet_from_jsval(
430 context: JSContext,
431 adopted_stylesheets: &mut Vec<Dom<CSSStyleSheet>>,
432 incoming_value: HandleValue,
433 owner: &StyleSheetListOwner,
434 ) -> ErrorResult {
435 let maybe_stylesheets =
436 Vec::<DomRoot<CSSStyleSheet>>::safe_from_jsval(context, incoming_value, ());
437
438 match maybe_stylesheets {
439 Ok(ConversionResult::Success(stylesheets)) => {
440 rooted_vec!(let stylesheets <- stylesheets.to_owned().iter().map(|s| s.as_traced()));
441
442 DocumentOrShadowRoot::set_adopted_stylesheet(
443 adopted_stylesheets,
444 &stylesheets,
445 owner,
446 )
447 },
448 Ok(ConversionResult::Failure(msg)) => Err(Error::Type(msg.to_string())),
449 Err(_) => Err(Error::Type(
450 "The provided value is not a sequence of 'CSSStylesheet'.".to_owned(),
451 )),
452 }
453 }
454}