use std::fmt;
use euclid::default::Point2D;
use script_layout_interface::{NodesFromPointQueryType, QueryMsg};
use script_traits::UntrustedNodeAddress;
use servo_arc::Arc;
use servo_atoms::Atom;
use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
use style::media_queries::MediaList;
use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
use style::stylesheets::scope_rule::ImplicitScopeRoot;
use style::stylesheets::{Stylesheet, StylesheetContents};
use super::bindings::trace::HashMapTracedValues;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::element::Element;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{self, Node, VecPreOrderInsertionHelper};
use crate::dom::window::Window;
use crate::script_runtime::CanGc;
use crate::stylesheet_set::StylesheetSetRef;
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[crown::unrooted_must_root_lint::must_root]
pub struct StyleSheetInDocument {
#[ignore_malloc_size_of = "Arc"]
#[no_trace]
pub sheet: Arc<Stylesheet>,
pub owner: Dom<Element>,
}
impl style_malloc_size_of::MallocSizeOf for StyleSheetInDocument {
fn size_of(&self, ops: &mut style_malloc_size_of::MallocSizeOfOps) -> usize {
<StyleSheetInDocument as malloc_size_of::MallocSizeOf>::size_of(self, ops)
}
}
impl fmt::Debug for StyleSheetInDocument {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.sheet.fmt(formatter)
}
}
impl PartialEq for StyleSheetInDocument {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.sheet, &other.sheet)
}
}
impl ToMediaListKey for StyleSheetInDocument {
fn to_media_list_key(&self) -> MediaListKey {
self.sheet.contents.to_media_list_key()
}
}
impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument {
fn enabled(&self) -> bool {
self.sheet.enabled()
}
fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
self.sheet.media(guard)
}
fn contents(&self) -> &StylesheetContents {
self.sheet.contents()
}
fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {
None
}
}
#[crown::unrooted_must_root_lint::must_root]
#[derive(JSTraceable, MallocSizeOf)]
pub struct DocumentOrShadowRoot {
window: Dom<Window>,
}
impl DocumentOrShadowRoot {
pub fn new(window: &Window) -> Self {
Self {
window: Dom::from_ref(window),
}
}
pub fn nodes_from_point(
&self,
client_point: &Point2D<f32>,
query_type: NodesFromPointQueryType,
can_gc: CanGc,
) -> Vec<UntrustedNodeAddress> {
if !self
.window
.layout_reflow(QueryMsg::NodesFromPointQuery, can_gc)
{
return vec![];
};
self.window
.layout()
.query_nodes_from_point(*client_point, query_type)
}
#[allow(unsafe_code)]
pub fn element_from_point(
&self,
x: Finite<f64>,
y: Finite<f64>,
document_element: Option<DomRoot<Element>>,
has_browsing_context: bool,
can_gc: CanGc,
) -> Option<DomRoot<Element>> {
let x = *x as f32;
let y = *y as f32;
let point = &Point2D::new(x, y);
let viewport = self.window.window_size().initial_viewport;
if !has_browsing_context {
return None;
}
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
return None;
}
match self
.nodes_from_point(point, NodesFromPointQueryType::Topmost, can_gc)
.first()
{
Some(address) => {
let node = unsafe { node::from_untrusted_node_address(*address) };
let parent_node = node.GetParentNode().unwrap();
let element_ref = node
.downcast::<Element>()
.unwrap_or_else(|| parent_node.downcast::<Element>().unwrap());
Some(DomRoot::from_ref(element_ref))
},
None => document_element,
}
}
#[allow(unsafe_code)]
pub fn elements_from_point(
&self,
x: Finite<f64>,
y: Finite<f64>,
document_element: Option<DomRoot<Element>>,
has_browsing_context: bool,
can_gc: CanGc,
) -> Vec<DomRoot<Element>> {
let x = *x as f32;
let y = *y as f32;
let point = &Point2D::new(x, y);
let viewport = self.window.window_size().initial_viewport;
if !has_browsing_context {
return vec![];
}
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
return vec![];
}
let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All, can_gc);
let mut elements: Vec<DomRoot<Element>> = nodes
.iter()
.flat_map(|&untrusted_node_address| {
let node = unsafe { node::from_untrusted_node_address(untrusted_node_address) };
DomRoot::downcast::<Element>(node)
})
.collect();
if let Some(root_element) = document_element {
if elements.last() != Some(&root_element) {
elements.push(root_element);
}
}
elements
}
pub fn get_active_element(
&self,
focused_element: Option<DomRoot<Element>>,
body: Option<DomRoot<HTMLElement>>,
document_element: Option<DomRoot<Element>>,
) -> Option<DomRoot<Element>> {
match focused_element {
Some(element) => Some(element), None => match body {
Some(body) => Some(DomRoot::upcast(body)),
None => document_element,
},
}
}
#[allow(crown::unrooted_must_root)] pub fn remove_stylesheet(
owner: &Element,
s: &Arc<Stylesheet>,
mut stylesheets: StylesheetSetRef<StyleSheetInDocument>,
) {
let guard = s.shared_lock.read();
stylesheets.remove_stylesheet(
None,
StyleSheetInDocument {
sheet: s.clone(),
owner: Dom::from_ref(owner),
},
&guard,
);
}
#[allow(crown::unrooted_must_root)] pub fn add_stylesheet(
owner: &Element,
mut stylesheets: StylesheetSetRef<StyleSheetInDocument>,
sheet: Arc<Stylesheet>,
insertion_point: Option<StyleSheetInDocument>,
style_shared_lock: &StyleSharedRwLock,
) {
debug_assert!(owner.as_stylesheet_owner().is_some(), "Wat");
let sheet = StyleSheetInDocument {
sheet,
owner: Dom::from_ref(owner),
};
let guard = style_shared_lock.read();
match insertion_point {
Some(ip) => {
stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
},
None => {
stylesheets.append_stylesheet(None, sheet, &guard);
},
}
}
pub fn unregister_named_element(
&self,
id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>>>,
to_unregister: &Element,
id: &Atom,
) {
debug!(
"Removing named element {:p}: {:p} id={}",
self, to_unregister, id
);
let mut id_map = id_map.borrow_mut();
let is_empty = match id_map.get_mut(id) {
None => false,
Some(elements) => {
let position = elements
.iter()
.position(|element| &**element == to_unregister)
.expect("This element should be in registered.");
elements.remove(position);
elements.is_empty()
},
};
if is_empty {
id_map.remove(id);
}
}
pub fn register_named_element(
&self,
id_map: &DomRefCell<HashMapTracedValues<Atom, Vec<Dom<Element>>>>,
element: &Element,
id: &Atom,
root: DomRoot<Node>,
) {
debug!("Adding named element {:p}: {:p} id={}", self, element, id);
assert!(element.upcast::<Node>().is_connected());
assert!(!id.is_empty());
let mut id_map = id_map.borrow_mut();
let elements = id_map.entry(id.clone()).or_default();
elements.insert_pre_order(element, &root);
}
}