mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
This is the last big change necessary to create the `constellation_traits` crate. This moves the data structure for messages that originate from the `ScriptThread` and are sent to the `Contellation` to `constellation_traits`, effectively splitting `script_traits` in half. Before, `script_traits` was responsible for exposing the API of both the `ScriptThread` and the `Constellation` to the rest of Servo. - Data structures that are used by `ScriptToConstellationMsg` are moved to `constellation_traits`. The dependency graph looks a bit like this: `script_layout_interface` depends on `script_traits` depends on `constellation_traits` depends on `embedder_traits`. - Data structures that are used in the embedding layer (`UntrustedNodeAddress`, `CompositorHitTestResult`, `TouchEventResult` and `AnimationState`) are moved to embedder_traits, to avoid a dependency cycle between `webrender_traits` and `constellation_traits`. - Types dealing with MessagePorts and serialization are moved to `constellation_traits::message_port`. Testing: This is covered by existing tests as it just moves types around. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Signed-off-by: Martin Robinson <mrobinson@igalia.com>
325 lines
10 KiB
Rust
325 lines
10 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
use std::fmt;
|
|
|
|
use embedder_traits::UntrustedNodeAddress;
|
|
use euclid::default::Point2D;
|
|
use script_layout_interface::{NodesFromPointQueryType, QueryMsg};
|
|
use servo_arc::Arc;
|
|
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 stylo_atoms::Atom;
|
|
|
|
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::codegen::Bindings::ShadowRootBinding::ShadowRootMethods;
|
|
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::shadowroot::ShadowRoot;
|
|
use crate::dom::window::Window;
|
|
use crate::script_runtime::CanGc;
|
|
use crate::stylesheet_set::StylesheetSetRef;
|
|
|
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
|
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
|
pub(crate) struct StyleSheetInDocument {
|
|
#[ignore_malloc_size_of = "Arc"]
|
|
#[no_trace]
|
|
pub(crate) sheet: Arc<Stylesheet>,
|
|
pub(crate) owner: Dom<Element>,
|
|
}
|
|
|
|
// This is necessary because this type is contained within a Stylo type which needs
|
|
// Stylo's version of MallocSizeOf.
|
|
impl stylo_malloc_size_of::MallocSizeOf for StyleSheetInDocument {
|
|
fn size_of(&self, ops: &mut stylo_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
|
|
}
|
|
}
|
|
|
|
// https://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin
|
|
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
|
#[derive(JSTraceable, MallocSizeOf)]
|
|
pub(crate) struct DocumentOrShadowRoot {
|
|
window: Dom<Window>,
|
|
}
|
|
|
|
impl DocumentOrShadowRoot {
|
|
pub(crate) fn new(window: &Window) -> Self {
|
|
Self {
|
|
window: Dom::from_ref(window),
|
|
}
|
|
}
|
|
|
|
pub(crate) 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)]
|
|
// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
|
|
pub(crate) 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.viewport_details().size;
|
|
|
|
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 shadow_host = parent_node
|
|
.downcast::<ShadowRoot>()
|
|
.map(ShadowRootMethods::Host);
|
|
let element_ref = node
|
|
.downcast::<Element>()
|
|
.or(shadow_host.as_deref())
|
|
.unwrap_or_else(|| {
|
|
parent_node
|
|
.downcast::<Element>()
|
|
.expect("Hit node should have an element or shadowroot parent")
|
|
});
|
|
|
|
Some(DomRoot::from_ref(element_ref))
|
|
},
|
|
None => document_element,
|
|
}
|
|
}
|
|
|
|
#[allow(unsafe_code)]
|
|
// https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
|
|
pub(crate) 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.viewport_details().size;
|
|
|
|
if !has_browsing_context {
|
|
return vec![];
|
|
}
|
|
|
|
// Step 2
|
|
if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
|
|
return vec![];
|
|
}
|
|
|
|
// Step 1 and Step 3
|
|
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();
|
|
|
|
// Step 4
|
|
if let Some(root_element) = document_element {
|
|
if elements.last() != Some(&root_element) {
|
|
elements.push(root_element);
|
|
}
|
|
}
|
|
|
|
// Step 5
|
|
elements
|
|
}
|
|
|
|
// https://html.spec.whatwg.org/multipage/#dom-document-activeelement
|
|
pub(crate) fn get_active_element(
|
|
&self,
|
|
focused_element: Option<DomRoot<Element>>,
|
|
body: Option<DomRoot<HTMLElement>>,
|
|
document_element: Option<DomRoot<Element>>,
|
|
) -> Option<DomRoot<Element>> {
|
|
// TODO: Step 2.
|
|
|
|
match focused_element {
|
|
Some(element) => Some(element), // Step 3. and 4.
|
|
None => match body {
|
|
// Step 5.
|
|
Some(body) => Some(DomRoot::upcast(body)),
|
|
None => document_element,
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Remove a stylesheet owned by `owner` from the list of document sheets.
|
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
|
|
pub(crate) fn remove_stylesheet(
|
|
owner: &Element,
|
|
s: &Arc<Stylesheet>,
|
|
mut stylesheets: StylesheetSetRef<StyleSheetInDocument>,
|
|
) {
|
|
let guard = s.shared_lock.read();
|
|
|
|
// FIXME(emilio): Would be nice to remove the clone, etc.
|
|
stylesheets.remove_stylesheet(
|
|
None,
|
|
StyleSheetInDocument {
|
|
sheet: s.clone(),
|
|
owner: Dom::from_ref(owner),
|
|
},
|
|
&guard,
|
|
);
|
|
}
|
|
|
|
/// Add a stylesheet owned by `owner` to the list of document sheets, in the
|
|
/// correct tree position.
|
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))] // Owner needs to be rooted already necessarily.
|
|
pub(crate) 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);
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Remove any existing association between the provided id/name and any elements in this document.
|
|
pub(crate) 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);
|
|
}
|
|
}
|
|
|
|
/// Associate an element present in this document with the provided id/name.
|
|
pub(crate) 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_in_a_document_tree() ||
|
|
element.upcast::<Node>().is_in_a_shadow_tree()
|
|
);
|
|
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);
|
|
}
|
|
}
|