aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/layout_dom/element.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/layout_dom/element.rs')
-rw-r--r--components/script/layout_dom/element.rs908
1 files changed, 908 insertions, 0 deletions
diff --git a/components/script/layout_dom/element.rs b/components/script/layout_dom/element.rs
new file mode 100644
index 00000000000..9b9f8c4790b
--- /dev/null
+++ b/components/script/layout_dom/element.rs
@@ -0,0 +1,908 @@
+/* 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 crate::dom::bindings::inheritance::{
+ CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId,
+};
+use crate::dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId, TextTypeId};
+use crate::dom::bindings::root::LayoutDom;
+use crate::dom::characterdata::LayoutCharacterDataHelpers;
+use crate::dom::element::{Element, LayoutElementHelpers};
+use crate::dom::node::{LayoutNodeHelpers, NodeFlags};
+use atomic_refcell::{AtomicRef, AtomicRefMut};
+use html5ever::{LocalName, Namespace};
+use script_layout_interface::wrapper_traits::{
+ GetStyleAndOpaqueLayoutData, LayoutDataTrait, LayoutNode, PseudoElementType,
+ ThreadSafeLayoutElement, ThreadSafeLayoutNode,
+};
+use script_layout_interface::{LayoutNodeType, StyleAndOpaqueLayoutData, StyleData};
+use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
+use selectors::matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode};
+use selectors::sink::Push;
+use servo_arc::{Arc, ArcBorrow};
+use servo_atoms::Atom;
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::marker::PhantomData;
+use std::sync::atomic::Ordering;
+use style::animation::AnimationSetKey;
+use style::applicable_declarations::ApplicableDeclarationBlock;
+use style::attr::AttrValue;
+use style::context::SharedStyleContext;
+use style::data::ElementData;
+use style::dom::{DomChildren, LayoutIterator, TDocument, TElement, TNode, TShadowRoot};
+use style::element_state::*;
+use style::font_metrics::ServoMetricsProvider;
+use style::properties::PropertyDeclarationBlock;
+use style::selector_parser::{
+ extended_filtering, AttrValue as SelectorAttrValue, Lang, NonTSPseudoClass, PseudoElement,
+ SelectorImpl,
+};
+use style::shared_lock::Locked as StyleLocked;
+use style::values::{AtomIdent, AtomString};
+use style::CaseSensitivityExt;
+
+use crate::layout_dom::ServoLayoutNode;
+use crate::layout_dom::ServoShadowRoot;
+use crate::layout_dom::ServoThreadSafeLayoutNode;
+
+/// A wrapper around elements that ensures layout can only ever access safe properties.
+pub struct ServoLayoutElement<'dom, LayoutDataType: LayoutDataTrait> {
+ /// The wrapped private DOM Element.
+ element: LayoutDom<'dom, Element>,
+
+ /// A PhantomData that is used to track the type of the stored layout data.
+ phantom: PhantomData<LayoutDataType>,
+}
+
+// These impls are required because `derive` has trouble with PhantomData.
+// See https://github.com/rust-lang/rust/issues/52079
+impl<'dom, LayoutDataType: LayoutDataTrait> Clone for ServoLayoutElement<'dom, LayoutDataType> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl<'dom, LayoutDataType: LayoutDataTrait> Copy for ServoLayoutElement<'dom, LayoutDataType> {}
+impl<'dom, LayoutDataType: LayoutDataTrait> PartialEq for ServoLayoutElement<'dom, LayoutDataType> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.as_node() == other.as_node()
+ }
+}
+
+// `Hash` + `Eq` + `Debug` are required by ::style::dom::TElement.
+impl<'dom, LayoutDataType: LayoutDataTrait> Eq for ServoLayoutElement<'dom, LayoutDataType> {}
+impl<'dom, LayoutDataType: LayoutDataTrait> Hash for ServoLayoutElement<'dom, LayoutDataType> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.element.hash(state);
+ }
+}
+impl<'dom, LayoutDataType: LayoutDataTrait> fmt::Debug
+ for ServoLayoutElement<'dom, LayoutDataType>
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "<{}", self.element.local_name())?;
+ if let Some(id) = self.id() {
+ write!(f, " id={}", id)?;
+ }
+ write!(f, "> ({:#x})", self.as_node().opaque().0)
+ }
+}
+
+impl<'dom, LayoutDataType: LayoutDataTrait> ServoLayoutElement<'dom, LayoutDataType> {
+ pub(super) fn from_layout_js(el: LayoutDom<'dom, Element>) -> Self {
+ ServoLayoutElement {
+ element: el,
+ phantom: PhantomData,
+ }
+ }
+
+ #[inline]
+ fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
+ self.element.get_attr_for_layout(namespace, name)
+ }
+
+ #[inline]
+ fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str> {
+ self.element.get_attr_val_for_layout(namespace, name)
+ }
+
+ fn get_style_data(&self) -> Option<&StyleData> {
+ self.get_style_and_opaque_layout_data()
+ .map(|data| &data.style_data)
+ }
+
+ pub unsafe fn unset_snapshot_flags(&self) {
+ self.as_node()
+ .node
+ .set_flag(NodeFlags::HAS_SNAPSHOT | NodeFlags::HANDLED_SNAPSHOT, false);
+ }
+
+ pub unsafe fn set_has_snapshot(&self) {
+ self.as_node().node.set_flag(NodeFlags::HAS_SNAPSHOT, true);
+ }
+
+ /// Returns true if this element is the body child of an html element root element.
+ fn is_body_element_of_html_element_root(&self) -> bool {
+ if self.element.local_name() != &local_name!("body") {
+ return false;
+ }
+
+ self.parent_element()
+ .map(|element| {
+ element.is_root() && element.element.local_name() == &local_name!("html")
+ })
+ .unwrap_or(false)
+ }
+
+ /// Returns the parent element of this element, if it has one.
+ fn parent_element(&self) -> Option<Self> {
+ self.element
+ .upcast()
+ .composed_parent_node_ref()
+ .and_then(|node| node.downcast().map(ServoLayoutElement::from_layout_js))
+ }
+
+ fn is_root(&self) -> bool {
+ match self.as_node().parent_node() {
+ None => false,
+ Some(node) => match node.script_type_id() {
+ NodeTypeId::Document(_) => true,
+ _ => false,
+ },
+ }
+ }
+}
+
+impl<'dom, LayoutDataType: LayoutDataTrait> GetStyleAndOpaqueLayoutData<'dom>
+ for ServoLayoutElement<'dom, LayoutDataType>
+{
+ fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> {
+ self.as_node().get_style_and_opaque_layout_data()
+ }
+}
+
+impl<'dom, LayoutDataType: LayoutDataTrait> style::dom::TElement
+ for ServoLayoutElement<'dom, LayoutDataType>
+{
+ type ConcreteNode = ServoLayoutNode<'dom, LayoutDataType>;
+ type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>;
+
+ type FontMetricsProvider = ServoMetricsProvider;
+
+ fn as_node(&self) -> ServoLayoutNode<'dom, LayoutDataType> {
+ ServoLayoutNode::from_layout_js(self.element.upcast())
+ }
+
+ fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator> {
+ LayoutIterator(if let Some(shadow) = self.shadow_root() {
+ shadow.as_node().dom_children()
+ } else {
+ self.as_node().dom_children()
+ })
+ }
+
+ fn is_html_element(&self) -> bool {
+ self.element.is_html_element()
+ }
+
+ fn is_mathml_element(&self) -> bool {
+ *self.element.namespace() == ns!(mathml)
+ }
+
+ fn is_svg_element(&self) -> bool {
+ *self.element.namespace() == ns!(svg)
+ }
+
+ fn has_part_attr(&self) -> bool {
+ false
+ }
+
+ fn exports_any_part(&self) -> bool {
+ false
+ }
+
+ fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> {
+ unsafe {
+ (*self.element.style_attribute())
+ .as_ref()
+ .map(|x| x.borrow_arc())
+ }
+ }
+
+ fn may_have_animations(&self) -> bool {
+ true
+ }
+
+ fn animation_rule(
+ &self,
+ context: &SharedStyleContext,
+ ) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
+ let node = self.as_node();
+ let document = node.owner_doc();
+ context.animations.get_animation_declarations(
+ &AnimationSetKey::new_for_non_pseudo(node.opaque()),
+ context.current_time_for_animations,
+ document.style_shared_lock(),
+ )
+ }
+
+ fn transition_rule(
+ &self,
+ context: &SharedStyleContext,
+ ) -> Option<Arc<StyleLocked<PropertyDeclarationBlock>>> {
+ let node = self.as_node();
+ let document = node.owner_doc();
+ context.animations.get_transition_declarations(
+ &AnimationSetKey::new_for_non_pseudo(node.opaque()),
+ context.current_time_for_animations,
+ document.style_shared_lock(),
+ )
+ }
+
+ fn state(&self) -> ElementState {
+ self.element.get_state_for_layout()
+ }
+
+ #[inline]
+ fn has_attr(&self, namespace: &style::Namespace, attr: &style::LocalName) -> bool {
+ self.get_attr(&**namespace, &**attr).is_some()
+ }
+
+ #[inline]
+ fn id(&self) -> Option<&Atom> {
+ unsafe { (*self.element.id_attribute()).as_ref() }
+ }
+
+ #[inline(always)]
+ fn each_class<F>(&self, mut callback: F)
+ where
+ F: FnMut(&AtomIdent),
+ {
+ if let Some(ref classes) = self.element.get_classes_for_layout() {
+ for class in *classes {
+ callback(AtomIdent::cast(class))
+ }
+ }
+ }
+
+ fn has_dirty_descendants(&self) -> bool {
+ unsafe {
+ self.as_node()
+ .node
+ .get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)
+ }
+ }
+
+ fn has_snapshot(&self) -> bool {
+ unsafe { self.as_node().node.get_flag(NodeFlags::HAS_SNAPSHOT) }
+ }
+
+ fn handled_snapshot(&self) -> bool {
+ unsafe { self.as_node().node.get_flag(NodeFlags::HANDLED_SNAPSHOT) }
+ }
+
+ unsafe fn set_handled_snapshot(&self) {
+ self.as_node()
+ .node
+ .set_flag(NodeFlags::HANDLED_SNAPSHOT, true);
+ }
+
+ unsafe fn set_dirty_descendants(&self) {
+ debug_assert!(self.as_node().is_connected());
+ self.as_node()
+ .node
+ .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true)
+ }
+
+ unsafe fn unset_dirty_descendants(&self) {
+ self.as_node()
+ .node
+ .set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, false)
+ }
+
+ fn store_children_to_process(&self, n: isize) {
+ let data = self.get_style_data().unwrap();
+ data.parallel
+ .children_to_process
+ .store(n, Ordering::Relaxed);
+ }
+
+ fn did_process_child(&self) -> isize {
+ let data = self.get_style_data().unwrap();
+ let old_value = data
+ .parallel
+ .children_to_process
+ .fetch_sub(1, Ordering::Relaxed);
+ debug_assert!(old_value >= 1);
+ old_value - 1
+ }
+
+ unsafe fn clear_data(&self) {
+ if self.get_style_and_opaque_layout_data().is_some() {
+ drop(self.as_node().take_style_and_opaque_layout_data());
+ }
+ }
+
+ unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
+ self.as_node().initialize_data();
+ self.mutate_data().unwrap()
+ }
+
+ /// Whether there is an ElementData container.
+ fn has_data(&self) -> bool {
+ self.get_style_data().is_some()
+ }
+
+ /// Immutably borrows the ElementData.
+ fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
+ self.get_style_data().map(|data| data.element_data.borrow())
+ }
+
+ /// Mutably borrows the ElementData.
+ fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> {
+ self.get_style_data()
+ .map(|data| data.element_data.borrow_mut())
+ }
+
+ fn skip_item_display_fixup(&self) -> bool {
+ false
+ }
+
+ unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) {
+ self.element.insert_selector_flags(flags);
+ }
+
+ fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
+ self.element.has_selector_flags(flags)
+ }
+
+ fn has_animations(&self, context: &SharedStyleContext) -> bool {
+ // This is not used for pseudo elements currently so we can pass None.
+ return self.has_css_animations(context, /* pseudo_element = */ None) ||
+ self.has_css_transitions(context, /* pseudo_element = */ None);
+ }
+
+ fn has_css_animations(
+ &self,
+ context: &SharedStyleContext,
+ pseudo_element: Option<PseudoElement>,
+ ) -> bool {
+ let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
+ context.animations.has_active_animations(&key)
+ }
+
+ fn has_css_transitions(
+ &self,
+ context: &SharedStyleContext,
+ pseudo_element: Option<PseudoElement>,
+ ) -> bool {
+ let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);
+ context.animations.has_active_transitions(&key)
+ }
+
+ #[inline]
+ fn lang_attr(&self) -> Option<SelectorAttrValue> {
+ self.get_attr(&ns!(xml), &local_name!("lang"))
+ .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
+ .map(|v| SelectorAttrValue::from(v as &str))
+ }
+
+ fn match_element_lang(
+ &self,
+ override_lang: Option<Option<SelectorAttrValue>>,
+ value: &Lang,
+ ) -> bool {
+ // Servo supports :lang() from CSS Selectors 4, which can take a comma-
+ // separated list of language tags in the pseudo-class, and which
+ // performs RFC 4647 extended filtering matching on them.
+ //
+ // FIXME(heycam): This is wrong, since extended_filtering accepts
+ // a string containing commas (separating each language tag in
+ // a list) but the pseudo-class instead should be parsing and
+ // storing separate <ident> or <string>s for each language tag.
+ //
+ // FIXME(heycam): Look at `element`'s document's Content-Language
+ // HTTP header for language tags to match `value` against. To
+ // do this, we should make `get_lang_for_layout` return an Option,
+ // so we can decide when to fall back to the Content-Language check.
+ let element_lang = match override_lang {
+ Some(Some(lang)) => lang,
+ Some(None) => AtomString::default(),
+ None => AtomString::from(&*self.element.get_lang_for_layout()),
+ };
+ extended_filtering(&element_lang, &*value)
+ }
+
+ fn is_html_document_body_element(&self) -> bool {
+ self.is_body_element_of_html_element_root()
+ }
+
+ fn synthesize_presentational_hints_for_legacy_attributes<V>(
+ &self,
+ _visited_handling: VisitedHandlingMode,
+ hints: &mut V,
+ ) where
+ V: Push<ApplicableDeclarationBlock>,
+ {
+ self.element
+ .synthesize_presentational_hints_for_legacy_attributes(hints);
+ }
+
+ /// The shadow root this element is a host of.
+ fn shadow_root(&self) -> Option<ServoShadowRoot<'dom, LayoutDataType>> {
+ self.element
+ .get_shadow_root_for_layout()
+ .map(ServoShadowRoot::from_layout_js)
+ }
+
+ /// The shadow root which roots the subtree this element is contained in.
+ fn containing_shadow(&self) -> Option<ServoShadowRoot<'dom, LayoutDataType>> {
+ self.element
+ .upcast()
+ .containing_shadow_root_for_layout()
+ .map(ServoShadowRoot::from_layout_js)
+ }
+
+ fn local_name(&self) -> &LocalName {
+ self.element.local_name()
+ }
+
+ fn namespace(&self) -> &Namespace {
+ self.element.namespace()
+ }
+}
+
+impl<'dom, LayoutDataType: LayoutDataTrait> ::selectors::Element
+ for ServoLayoutElement<'dom, LayoutDataType>
+{
+ type Impl = SelectorImpl;
+
+ fn opaque(&self) -> ::selectors::OpaqueElement {
+ ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
+ }
+
+ fn parent_element(&self) -> Option<Self> {
+ ServoLayoutElement::parent_element(self)
+ }
+
+ fn parent_node_is_shadow_root(&self) -> bool {
+ match self.as_node().parent_node() {
+ None => false,
+ Some(node) => {
+ node.script_type_id() ==
+ NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot)
+ },
+ }
+ }
+
+ fn containing_shadow_host(&self) -> Option<Self> {
+ self.containing_shadow().map(|s| s.host())
+ }
+
+ fn prev_sibling_element(&self) -> Option<Self> {
+ let mut node = self.as_node();
+ while let Some(sibling) = node.prev_sibling() {
+ if let Some(element) = sibling.as_element() {
+ return Some(element);
+ }
+ node = sibling;
+ }
+ None
+ }
+
+ fn next_sibling_element(&self) -> Option<ServoLayoutElement<'dom, LayoutDataType>> {
+ let mut node = self.as_node();
+ while let Some(sibling) = node.next_sibling() {
+ if let Some(element) = sibling.as_element() {
+ return Some(element);
+ }
+ node = sibling;
+ }
+ None
+ }
+
+ fn attr_matches(
+ &self,
+ ns: &NamespaceConstraint<&style::Namespace>,
+ local_name: &style::LocalName,
+ operation: &AttrSelectorOperation<&AtomString>,
+ ) -> bool {
+ match *ns {
+ NamespaceConstraint::Specific(ref ns) => self
+ .get_attr_enum(ns, local_name)
+ .map_or(false, |value| value.eval_selector(operation)),
+ NamespaceConstraint::Any => self
+ .element
+ .get_attr_vals_for_layout(local_name)
+ .iter()
+ .any(|value| value.eval_selector(operation)),
+ }
+ }
+
+ fn is_root(&self) -> bool {
+ ServoLayoutElement::is_root(self)
+ }
+
+ fn is_empty(&self) -> bool {
+ self.as_node()
+ .dom_children()
+ .all(|node| match node.script_type_id() {
+ NodeTypeId::Element(..) => false,
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
+ node.node.downcast().unwrap().data_for_layout().is_empty()
+ },
+ _ => true,
+ })
+ }
+
+ #[inline]
+ fn has_local_name(&self, name: &LocalName) -> bool {
+ self.element.local_name() == name
+ }
+
+ #[inline]
+ fn has_namespace(&self, ns: &Namespace) -> bool {
+ self.element.namespace() == ns
+ }
+
+ #[inline]
+ fn is_same_type(&self, other: &Self) -> bool {
+ self.element.local_name() == other.element.local_name() &&
+ self.element.namespace() == other.element.namespace()
+ }
+
+ fn is_pseudo_element(&self) -> bool {
+ false
+ }
+
+ fn match_pseudo_element(
+ &self,
+ _pseudo: &PseudoElement,
+ _context: &mut MatchingContext<Self::Impl>,
+ ) -> bool {
+ false
+ }
+
+ fn match_non_ts_pseudo_class<F>(
+ &self,
+ pseudo_class: &NonTSPseudoClass,
+ _: &mut MatchingContext<Self::Impl>,
+ _: &mut F,
+ ) -> bool
+ where
+ F: FnMut(&Self, ElementSelectorFlags),
+ {
+ match *pseudo_class {
+ // https://github.com/servo/servo/issues/8718
+ NonTSPseudoClass::Link | NonTSPseudoClass::AnyLink => self.is_link(),
+ NonTSPseudoClass::Visited => false,
+
+ NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, &*lang),
+
+ NonTSPseudoClass::ServoNonZeroBorder => {
+ match self
+ .element
+ .get_attr_for_layout(&ns!(), &local_name!("border"))
+ {
+ None | Some(&AttrValue::UInt(_, 0)) => false,
+ _ => true,
+ }
+ },
+ NonTSPseudoClass::ReadOnly => !self
+ .element
+ .get_state_for_layout()
+ .contains(pseudo_class.state_flag()),
+
+ NonTSPseudoClass::Active |
+ NonTSPseudoClass::Focus |
+ NonTSPseudoClass::Fullscreen |
+ NonTSPseudoClass::Hover |
+ NonTSPseudoClass::Defined |
+ NonTSPseudoClass::Enabled |
+ NonTSPseudoClass::Disabled |
+ NonTSPseudoClass::Checked |
+ NonTSPseudoClass::Indeterminate |
+ NonTSPseudoClass::ReadWrite |
+ NonTSPseudoClass::PlaceholderShown |
+ NonTSPseudoClass::Target => self
+ .element
+ .get_state_for_layout()
+ .contains(pseudo_class.state_flag()),
+ }
+ }
+
+ #[inline]
+ fn is_link(&self) -> bool {
+ match self.as_node().script_type_id() {
+ // https://html.spec.whatwg.org/multipage/#selector-link
+ NodeTypeId::Element(ElementTypeId::HTMLElement(
+ HTMLElementTypeId::HTMLAnchorElement,
+ )) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
+ self.element
+ .get_attr_val_for_layout(&ns!(), &local_name!("href"))
+ .is_some()
+ },
+ _ => false,
+ }
+ }
+
+ #[inline]
+ fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
+ unsafe {
+ (*self.element.id_attribute())
+ .as_ref()
+ .map_or(false, |atom| case_sensitivity.eq_atom(atom, id))
+ }
+ }
+
+ #[inline]
+ fn is_part(&self, _name: &AtomIdent) -> bool {
+ false
+ }
+
+ fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
+ None
+ }
+
+ #[inline]
+ fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
+ self.element.has_class_for_layout(name, case_sensitivity)
+ }
+
+ fn is_html_slot_element(&self) -> bool {
+ self.element.is_html_element() && self.local_name() == &local_name!("slot")
+ }
+
+ fn is_html_element_in_html_document(&self) -> bool {
+ self.element.is_html_element() && self.as_node().owner_doc().is_html_document()
+ }
+}
+
+/// A wrapper around elements that ensures layout can only
+/// ever access safe properties and cannot race on elements.
+pub struct ServoThreadSafeLayoutElement<'dom, LayoutDataType: LayoutDataTrait> {
+ pub(super) element: ServoLayoutElement<'dom, LayoutDataType>,
+
+ /// The pseudo-element type, with (optionally)
+ /// a specified display value to override the stylesheet.
+ pub(super) pseudo: PseudoElementType,
+}
+
+// These impls are required because `derive` has trouble with PhantomData.
+// See https://github.com/rust-lang/rust/issues/52079
+impl<'dom, LayoutDataType: LayoutDataTrait> Clone
+ for ServoThreadSafeLayoutElement<'dom, LayoutDataType>
+{
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+impl<'dom, LayoutDataType: LayoutDataTrait> Copy
+ for ServoThreadSafeLayoutElement<'dom, LayoutDataType>
+{
+}
+impl<'dom, LayoutDataType: LayoutDataTrait> fmt::Debug
+ for ServoThreadSafeLayoutElement<'dom, LayoutDataType>
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.element.fmt(f)
+ }
+}
+
+impl<'dom, LayoutDataType: LayoutDataTrait> ThreadSafeLayoutElement<'dom>
+ for ServoThreadSafeLayoutElement<'dom, LayoutDataType>
+{
+ type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'dom, LayoutDataType>;
+ type ConcreteElement = ServoLayoutElement<'dom, LayoutDataType>;
+
+ fn as_node(&self) -> ServoThreadSafeLayoutNode<'dom, LayoutDataType> {
+ ServoThreadSafeLayoutNode {
+ node: self.element.as_node(),
+ pseudo: self.pseudo.clone(),
+ }
+ }
+
+ fn get_pseudo_element_type(&self) -> PseudoElementType {
+ self.pseudo
+ }
+
+ fn with_pseudo(&self, pseudo: PseudoElementType) -> Self {
+ ServoThreadSafeLayoutElement {
+ element: self.element.clone(),
+ pseudo,
+ }
+ }
+
+ fn type_id(&self) -> Option<LayoutNodeType> {
+ self.as_node().type_id()
+ }
+
+ unsafe fn unsafe_get(self) -> ServoLayoutElement<'dom, LayoutDataType> {
+ self.element
+ }
+
+ fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
+ self.element.get_attr_enum(namespace, name)
+ }
+
+ fn get_attr<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str> {
+ self.element.get_attr(namespace, name)
+ }
+
+ fn style_data(&self) -> AtomicRef<ElementData> {
+ self.element.borrow_data().expect("Unstyled layout node?")
+ }
+
+ fn is_shadow_host(&self) -> bool {
+ self.element.shadow_root().is_some()
+ }
+
+ fn is_body_element_of_html_element_root(&self) -> bool {
+ self.element.is_html_document_body_element()
+ }
+}
+
+/// This implementation of `::selectors::Element` is used for implementing lazy
+/// pseudo-elements.
+///
+/// Lazy pseudo-elements in Servo only allows selectors using safe properties,
+/// i.e., local_name, attributes, so they can only be used for **private**
+/// pseudo-elements (like `::-servo-details-content`).
+///
+/// Probably a few more of this functions can be implemented (like `has_class`, etc.),
+/// but they have no use right now.
+///
+/// Note that the element implementation is needed only for selector matching,
+/// not for inheritance (styles are inherited appropriately).
+impl<'dom, LayoutDataType: LayoutDataTrait> ::selectors::Element
+ for ServoThreadSafeLayoutElement<'dom, LayoutDataType>
+{
+ type Impl = SelectorImpl;
+
+ fn opaque(&self) -> ::selectors::OpaqueElement {
+ ::selectors::OpaqueElement::new(unsafe { &*(self.as_node().opaque().0 as *const ()) })
+ }
+
+ fn is_pseudo_element(&self) -> bool {
+ false
+ }
+
+ fn parent_element(&self) -> Option<Self> {
+ warn!("ServoThreadSafeLayoutElement::parent_element called");
+ None
+ }
+
+ fn parent_node_is_shadow_root(&self) -> bool {
+ false
+ }
+
+ fn containing_shadow_host(&self) -> Option<Self> {
+ None
+ }
+
+ // Skips non-element nodes
+ fn prev_sibling_element(&self) -> Option<Self> {
+ warn!("ServoThreadSafeLayoutElement::prev_sibling_element called");
+ None
+ }
+
+ // Skips non-element nodes
+ fn next_sibling_element(&self) -> Option<Self> {
+ warn!("ServoThreadSafeLayoutElement::next_sibling_element called");
+ None
+ }
+
+ fn is_html_slot_element(&self) -> bool {
+ self.element.is_html_slot_element()
+ }
+
+ fn is_html_element_in_html_document(&self) -> bool {
+ debug!("ServoThreadSafeLayoutElement::is_html_element_in_html_document called");
+ true
+ }
+
+ #[inline]
+ fn has_local_name(&self, name: &LocalName) -> bool {
+ self.element.local_name() == name
+ }
+
+ #[inline]
+ fn has_namespace(&self, ns: &Namespace) -> bool {
+ self.element.namespace() == ns
+ }
+
+ #[inline]
+ fn is_same_type(&self, other: &Self) -> bool {
+ self.element.local_name() == other.element.local_name() &&
+ self.element.namespace() == other.element.namespace()
+ }
+
+ fn match_pseudo_element(
+ &self,
+ _pseudo: &PseudoElement,
+ _context: &mut MatchingContext<Self::Impl>,
+ ) -> bool {
+ false
+ }
+
+ fn attr_matches(
+ &self,
+ ns: &NamespaceConstraint<&style::Namespace>,
+ local_name: &style::LocalName,
+ operation: &AttrSelectorOperation<&AtomString>,
+ ) -> bool {
+ match *ns {
+ NamespaceConstraint::Specific(ref ns) => self
+ .get_attr_enum(ns, local_name)
+ .map_or(false, |value| value.eval_selector(operation)),
+ NamespaceConstraint::Any => self
+ .element
+ .element
+ .get_attr_vals_for_layout(local_name)
+ .iter()
+ .any(|v| v.eval_selector(operation)),
+ }
+ }
+
+ fn match_non_ts_pseudo_class<F>(
+ &self,
+ _: &NonTSPseudoClass,
+ _: &mut MatchingContext<Self::Impl>,
+ _: &mut F,
+ ) -> bool
+ where
+ F: FnMut(&Self, ElementSelectorFlags),
+ {
+ // NB: This could maybe be implemented
+ warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
+ false
+ }
+
+ fn is_link(&self) -> bool {
+ warn!("ServoThreadSafeLayoutElement::is_link called");
+ false
+ }
+
+ fn has_id(&self, _id: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
+ debug!("ServoThreadSafeLayoutElement::has_id called");
+ false
+ }
+
+ #[inline]
+ fn is_part(&self, _name: &AtomIdent) -> bool {
+ debug!("ServoThreadSafeLayoutElement::is_part called");
+ false
+ }
+
+ fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
+ debug!("ServoThreadSafeLayoutElement::imported_part called");
+ None
+ }
+
+ fn has_class(&self, _name: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
+ debug!("ServoThreadSafeLayoutElement::has_class called");
+ false
+ }
+
+ fn is_empty(&self) -> bool {
+ warn!("ServoThreadSafeLayoutElement::is_empty called");
+ false
+ }
+
+ fn is_root(&self) -> bool {
+ warn!("ServoThreadSafeLayoutElement::is_root called");
+ false
+ }
+}
+
+impl<'dom, LayoutDataType: LayoutDataTrait> GetStyleAndOpaqueLayoutData<'dom>
+ for ServoThreadSafeLayoutElement<'dom, LayoutDataType>
+{
+ fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> {
+ self.element.as_node().get_style_and_opaque_layout_data()
+ }
+}