summaryrefslogtreecommitdiffstats
path: root/servo/components/style/dom.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--servo/components/style/dom.rs947
1 files changed, 947 insertions, 0 deletions
diff --git a/servo/components/style/dom.rs b/servo/components/style/dom.rs
new file mode 100644
index 0000000000..cffc24f6cc
--- /dev/null
+++ b/servo/components/style/dom.rs
@@ -0,0 +1,947 @@
+/* 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/. */
+
+//! Types and traits used to access the DOM from style calculation.
+
+#![allow(unsafe_code)]
+#![deny(missing_docs)]
+
+use crate::applicable_declarations::ApplicableDeclarationBlock;
+use crate::context::SharedStyleContext;
+#[cfg(feature = "gecko")]
+use crate::context::{PostAnimationTasks, UpdateAnimationsTasks};
+use crate::data::ElementData;
+use crate::media_queries::Device;
+use crate::properties::{AnimationDeclarations, ComputedValues, PropertyDeclarationBlock};
+use crate::selector_parser::{AttrValue, Lang, PseudoElement, SelectorImpl};
+use crate::shared_lock::{Locked, SharedRwLock};
+use crate::stylist::CascadeData;
+use crate::values::computed::Display;
+use crate::values::AtomIdent;
+use crate::{LocalName, Namespace, WeakAtom};
+use atomic_refcell::{AtomicRef, AtomicRefMut};
+use dom::ElementState;
+use selectors::matching::{QuirksMode, VisitedHandlingMode};
+use selectors::sink::Push;
+use selectors::Element as SelectorsElement;
+use servo_arc::{Arc, ArcBorrow};
+use std::fmt;
+use std::fmt::Debug;
+use std::hash::Hash;
+use std::ops::Deref;
+
+pub use style_traits::dom::OpaqueNode;
+
+/// Simple trait to provide basic information about the type of an element.
+///
+/// We avoid exposing the full type id, since computing it in the general case
+/// would be difficult for Gecko nodes.
+pub trait NodeInfo {
+ /// Whether this node is an element.
+ fn is_element(&self) -> bool;
+ /// Whether this node is a text node.
+ fn is_text_node(&self) -> bool;
+}
+
+/// A node iterator that only returns node that don't need layout.
+pub struct LayoutIterator<T>(pub T);
+
+impl<T, N> Iterator for LayoutIterator<T>
+where
+ T: Iterator<Item = N>,
+ N: NodeInfo,
+{
+ type Item = N;
+
+ fn next(&mut self) -> Option<N> {
+ loop {
+ let n = self.0.next()?;
+ // Filter out nodes that layout should ignore.
+ if n.is_text_node() || n.is_element() {
+ return Some(n);
+ }
+ }
+ }
+}
+
+/// An iterator over the DOM children of a node.
+pub struct DomChildren<N>(Option<N>);
+impl<N> Iterator for DomChildren<N>
+where
+ N: TNode,
+{
+ type Item = N;
+
+ fn next(&mut self) -> Option<N> {
+ let n = self.0.take()?;
+ self.0 = n.next_sibling();
+ Some(n)
+ }
+}
+
+/// An iterator over the DOM descendants of a node in pre-order.
+pub struct DomDescendants<N> {
+ previous: Option<N>,
+ scope: N,
+}
+
+impl<N> Iterator for DomDescendants<N>
+where
+ N: TNode,
+{
+ type Item = N;
+
+ #[inline]
+ fn next(&mut self) -> Option<N> {
+ let prev = self.previous.take()?;
+ self.previous = prev.next_in_preorder(self.scope);
+ self.previous
+ }
+}
+
+/// The `TDocument` trait, to represent a document node.
+pub trait TDocument: Sized + Copy + Clone {
+ /// The concrete `TNode` type.
+ type ConcreteNode: TNode<ConcreteDocument = Self>;
+
+ /// Get this document as a `TNode`.
+ fn as_node(&self) -> Self::ConcreteNode;
+
+ /// Returns whether this document is an HTML document.
+ fn is_html_document(&self) -> bool;
+
+ /// Returns the quirks mode of this document.
+ fn quirks_mode(&self) -> QuirksMode;
+
+ /// Get a list of elements with a given ID in this document, sorted by
+ /// tree position.
+ ///
+ /// Can return an error to signal that this list is not available, or also
+ /// return an empty slice.
+ fn elements_with_id<'a>(
+ &self,
+ _id: &AtomIdent,
+ ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
+ where
+ Self: 'a,
+ {
+ Err(())
+ }
+
+ /// This document's shared lock.
+ fn shared_lock(&self) -> &SharedRwLock;
+}
+
+/// The `TNode` trait. This is the main generic trait over which the style
+/// system can be implemented.
+pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
+ /// The concrete `TElement` type.
+ type ConcreteElement: TElement<ConcreteNode = Self>;
+
+ /// The concrete `TDocument` type.
+ type ConcreteDocument: TDocument<ConcreteNode = Self>;
+
+ /// The concrete `TShadowRoot` type.
+ type ConcreteShadowRoot: TShadowRoot<ConcreteNode = Self>;
+
+ /// Get this node's parent node.
+ fn parent_node(&self) -> Option<Self>;
+
+ /// Get this node's first child.
+ fn first_child(&self) -> Option<Self>;
+
+ /// Get this node's last child.
+ fn last_child(&self) -> Option<Self>;
+
+ /// Get this node's previous sibling.
+ fn prev_sibling(&self) -> Option<Self>;
+
+ /// Get this node's next sibling.
+ fn next_sibling(&self) -> Option<Self>;
+
+ /// Get the owner document of this node.
+ fn owner_doc(&self) -> Self::ConcreteDocument;
+
+ /// Iterate over the DOM children of a node.
+ #[inline(always)]
+ fn dom_children(&self) -> DomChildren<Self> {
+ DomChildren(self.first_child())
+ }
+
+ /// Returns whether the node is attached to a document.
+ fn is_in_document(&self) -> bool;
+
+ /// Iterate over the DOM children of a node, in preorder.
+ #[inline(always)]
+ fn dom_descendants(&self) -> DomDescendants<Self> {
+ DomDescendants {
+ previous: Some(*self),
+ scope: *self,
+ }
+ }
+
+ /// Returns the next node after this one, in a pre-order tree-traversal of
+ /// the subtree rooted at scoped_to.
+ #[inline]
+ fn next_in_preorder(&self, scoped_to: Self) -> Option<Self> {
+ if let Some(c) = self.first_child() {
+ return Some(c);
+ }
+
+ let mut current = *self;
+ loop {
+ if current == scoped_to {
+ return None;
+ }
+
+ if let Some(s) = current.next_sibling() {
+ return Some(s);
+ }
+
+ debug_assert!(
+ current.parent_node().is_some(),
+ "Not a descendant of the scope?"
+ );
+ current = current.parent_node()?;
+ }
+ }
+
+ /// Get this node's parent element from the perspective of a restyle
+ /// traversal.
+ fn traversal_parent(&self) -> Option<Self::ConcreteElement>;
+
+ /// Get this node's parent element if present.
+ fn parent_element(&self) -> Option<Self::ConcreteElement> {
+ self.parent_node().and_then(|n| n.as_element())
+ }
+
+ /// Get this node's parent element, or shadow host if it's a shadow root.
+ fn parent_element_or_host(&self) -> Option<Self::ConcreteElement> {
+ let parent = self.parent_node()?;
+ if let Some(e) = parent.as_element() {
+ return Some(e);
+ }
+ if let Some(root) = parent.as_shadow_root() {
+ return Some(root.host());
+ }
+ None
+ }
+
+ /// Converts self into an `OpaqueNode`.
+ fn opaque(&self) -> OpaqueNode;
+
+ /// A debug id, only useful, mm... for debugging.
+ fn debug_id(self) -> usize;
+
+ /// Get this node as an element, if it's one.
+ fn as_element(&self) -> Option<Self::ConcreteElement>;
+
+ /// Get this node as a document, if it's one.
+ fn as_document(&self) -> Option<Self::ConcreteDocument>;
+
+ /// Get this node as a ShadowRoot, if it's one.
+ fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot>;
+}
+
+/// Wrapper to output the subtree rather than the single node when formatting
+/// for Debug.
+pub struct ShowSubtree<N: TNode>(pub N);
+impl<N: TNode> Debug for ShowSubtree<N> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "DOM Subtree:")?;
+ fmt_subtree(f, &|f, n| write!(f, "{:?}", n), self.0, 1)
+ }
+}
+
+/// Wrapper to output the subtree along with the ElementData when formatting
+/// for Debug.
+pub struct ShowSubtreeData<N: TNode>(pub N);
+impl<N: TNode> Debug for ShowSubtreeData<N> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "DOM Subtree:")?;
+ fmt_subtree(f, &|f, n| fmt_with_data(f, n), self.0, 1)
+ }
+}
+
+/// Wrapper to output the subtree along with the ElementData and primary
+/// ComputedValues when formatting for Debug. This is extremely verbose.
+#[cfg(feature = "servo")]
+pub struct ShowSubtreeDataAndPrimaryValues<N: TNode>(pub N);
+#[cfg(feature = "servo")]
+impl<N: TNode> Debug for ShowSubtreeDataAndPrimaryValues<N> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "DOM Subtree:")?;
+ fmt_subtree(f, &|f, n| fmt_with_data_and_primary_values(f, n), self.0, 1)
+ }
+}
+
+fn fmt_with_data<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {
+ if let Some(el) = n.as_element() {
+ write!(
+ f,
+ "{:?} dd={} aodd={} data={:?}",
+ el,
+ el.has_dirty_descendants(),
+ el.has_animation_only_dirty_descendants(),
+ el.borrow_data(),
+ )
+ } else {
+ write!(f, "{:?}", n)
+ }
+}
+
+#[cfg(feature = "servo")]
+fn fmt_with_data_and_primary_values<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {
+ if let Some(el) = n.as_element() {
+ let dd = el.has_dirty_descendants();
+ let aodd = el.has_animation_only_dirty_descendants();
+ let data = el.borrow_data();
+ let values = data.as_ref().and_then(|d| d.styles.get_primary());
+ write!(
+ f,
+ "{:?} dd={} aodd={} data={:?} values={:?}",
+ el, dd, aodd, &data, values
+ )
+ } else {
+ write!(f, "{:?}", n)
+ }
+}
+
+fn fmt_subtree<F, N: TNode>(f: &mut fmt::Formatter, stringify: &F, n: N, indent: u32) -> fmt::Result
+where
+ F: Fn(&mut fmt::Formatter, N) -> fmt::Result,
+{
+ for _ in 0..indent {
+ write!(f, " ")?;
+ }
+ stringify(f, n)?;
+ if let Some(e) = n.as_element() {
+ for kid in e.traversal_children() {
+ writeln!(f, "")?;
+ fmt_subtree(f, stringify, kid, indent + 1)?;
+ }
+ }
+
+ Ok(())
+}
+
+/// The ShadowRoot trait.
+pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq {
+ /// The concrete node type.
+ type ConcreteNode: TNode<ConcreteShadowRoot = Self>;
+
+ /// Get this ShadowRoot as a node.
+ fn as_node(&self) -> Self::ConcreteNode;
+
+ /// Get the shadow host that hosts this ShadowRoot.
+ fn host(&self) -> <Self::ConcreteNode as TNode>::ConcreteElement;
+
+ /// Get the style data for this ShadowRoot.
+ fn style_data<'a>(&self) -> Option<&'a CascadeData>
+ where
+ Self: 'a;
+
+ /// Get the list of shadow parts for this shadow root.
+ fn parts<'a>(&self) -> &[<Self::ConcreteNode as TNode>::ConcreteElement]
+ where
+ Self: 'a,
+ {
+ &[]
+ }
+
+ /// Get a list of elements with a given ID in this shadow root, sorted by
+ /// tree position.
+ ///
+ /// Can return an error to signal that this list is not available, or also
+ /// return an empty slice.
+ fn elements_with_id<'a>(
+ &self,
+ _id: &AtomIdent,
+ ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
+ where
+ Self: 'a,
+ {
+ Err(())
+ }
+}
+
+/// The element trait, the main abstraction the style crate acts over.
+pub trait TElement:
+ Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + SelectorsElement<Impl = SelectorImpl>
+{
+ /// The concrete node type.
+ type ConcreteNode: TNode<ConcreteElement = Self>;
+
+ /// A concrete children iterator type in order to iterate over the `Node`s.
+ ///
+ /// TODO(emilio): We should eventually replace this with the `impl Trait`
+ /// syntax.
+ type TraversalChildrenIterator: Iterator<Item = Self::ConcreteNode>;
+
+ /// Get this element as a node.
+ fn as_node(&self) -> Self::ConcreteNode;
+
+ /// A debug-only check that the device's owner doc matches the actual doc
+ /// we're the root of.
+ ///
+ /// Otherwise we may set document-level state incorrectly, like the root
+ /// font-size used for rem units.
+ fn owner_doc_matches_for_testing(&self, _: &Device) -> bool {
+ true
+ }
+
+ /// Whether this element should match user and content rules.
+ ///
+ /// We use this for Native Anonymous Content in Gecko.
+ fn matches_user_and_content_rules(&self) -> bool {
+ true
+ }
+
+ /// Returns the depth of this element in the DOM.
+ fn depth(&self) -> usize {
+ let mut depth = 0;
+ let mut curr = *self;
+ while let Some(parent) = curr.traversal_parent() {
+ depth += 1;
+ curr = parent;
+ }
+
+ depth
+ }
+
+ /// Get this node's parent element from the perspective of a restyle
+ /// traversal.
+ fn traversal_parent(&self) -> Option<Self> {
+ self.as_node().traversal_parent()
+ }
+
+ /// Get this node's children from the perspective of a restyle traversal.
+ fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator>;
+
+ /// Returns the parent element we should inherit from.
+ ///
+ /// This is pretty much always the parent element itself, except in the case
+ /// of Gecko's Native Anonymous Content, which uses the traversal parent
+ /// (i.e. the flattened tree parent) and which also may need to find the
+ /// closest non-NAC ancestor.
+ fn inheritance_parent(&self) -> Option<Self> {
+ self.parent_element()
+ }
+
+ /// The ::before pseudo-element of this element, if it exists.
+ fn before_pseudo_element(&self) -> Option<Self> {
+ None
+ }
+
+ /// The ::after pseudo-element of this element, if it exists.
+ fn after_pseudo_element(&self) -> Option<Self> {
+ None
+ }
+
+ /// The ::marker pseudo-element of this element, if it exists.
+ fn marker_pseudo_element(&self) -> Option<Self> {
+ None
+ }
+
+ /// Execute `f` for each anonymous content child (apart from ::before and
+ /// ::after) whose originating element is `self`.
+ fn each_anonymous_content_child<F>(&self, _f: F)
+ where
+ F: FnMut(Self),
+ {
+ }
+
+ /// Return whether this element is an element in the HTML namespace.
+ fn is_html_element(&self) -> bool;
+
+ /// Return whether this element is an element in the MathML namespace.
+ fn is_mathml_element(&self) -> bool;
+
+ /// Return whether this element is an element in the SVG namespace.
+ fn is_svg_element(&self) -> bool;
+
+ /// Return whether this element is an element in the XUL namespace.
+ fn is_xul_element(&self) -> bool {
+ false
+ }
+
+ /// Return the list of slotted nodes of this node.
+ fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
+ &[]
+ }
+
+ /// Get this element's style attribute.
+ fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>;
+
+ /// Unset the style attribute's dirty bit.
+ /// Servo doesn't need to manage ditry bit for style attribute.
+ fn unset_dirty_style_attribute(&self) {}
+
+ /// Get this element's SMIL override declarations.
+ fn smil_override(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
+ None
+ }
+
+ /// Get the combined animation and transition rules.
+ ///
+ /// FIXME(emilio): Is this really useful?
+ fn animation_declarations(&self, context: &SharedStyleContext) -> AnimationDeclarations {
+ if !self.may_have_animations() {
+ return Default::default();
+ }
+
+ AnimationDeclarations {
+ animations: self.animation_rule(context),
+ transitions: self.transition_rule(context),
+ }
+ }
+
+ /// Get this element's animation rule.
+ fn animation_rule(
+ &self,
+ _: &SharedStyleContext,
+ ) -> Option<Arc<Locked<PropertyDeclarationBlock>>>;
+
+ /// Get this element's transition rule.
+ fn transition_rule(
+ &self,
+ context: &SharedStyleContext,
+ ) -> Option<Arc<Locked<PropertyDeclarationBlock>>>;
+
+ /// Get this element's state, for non-tree-structural pseudos.
+ fn state(&self) -> ElementState;
+
+ /// Whether this element has an attribute with a given namespace.
+ fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool;
+
+ /// Returns whether this element has a `part` attribute.
+ fn has_part_attr(&self) -> bool;
+
+ /// Returns whether this element exports any part from its shadow tree.
+ fn exports_any_part(&self) -> bool;
+
+ /// The ID for this element.
+ fn id(&self) -> Option<&WeakAtom>;
+
+ /// Internal iterator for the classes of this element.
+ fn each_class<F>(&self, callback: F)
+ where
+ F: FnMut(&AtomIdent);
+
+ /// Internal iterator for the part names of this element.
+ fn each_part<F>(&self, _callback: F)
+ where
+ F: FnMut(&AtomIdent),
+ {
+ }
+
+ /// Internal iterator for the attribute names of this element.
+ fn each_attr_name<F>(&self, callback: F)
+ where
+ F: FnMut(&AtomIdent);
+
+ /// Internal iterator for the part names that this element exports for a
+ /// given part name.
+ fn each_exported_part<F>(&self, _name: &AtomIdent, _callback: F)
+ where
+ F: FnMut(&AtomIdent),
+ {
+ }
+
+ /// Whether a given element may generate a pseudo-element.
+ ///
+ /// This is useful to avoid computing, for example, pseudo styles for
+ /// `::-first-line` or `::-first-letter`, when we know it won't affect us.
+ ///
+ /// TODO(emilio, bz): actually implement the logic for it.
+ fn may_generate_pseudo(&self, pseudo: &PseudoElement, _primary_style: &ComputedValues) -> bool {
+ // ::before/::after are always supported for now, though we could try to
+ // optimize out leaf elements.
+
+ // ::first-letter and ::first-line are only supported for block-inside
+ // things, and only in Gecko, not Servo. Unfortunately, Gecko has
+ // block-inside things that might have any computed display value due to
+ // things like fieldsets, legends, etc. Need to figure out how this
+ // should work.
+ debug_assert!(
+ pseudo.is_eager(),
+ "Someone called may_generate_pseudo with a non-eager pseudo."
+ );
+ true
+ }
+
+ /// Returns true if this element may have a descendant needing style processing.
+ ///
+ /// Note that we cannot guarantee the existence of such an element, because
+ /// it may have been removed from the DOM between marking it for restyle and
+ /// the actual restyle traversal.
+ fn has_dirty_descendants(&self) -> bool;
+
+ /// Returns whether state or attributes that may change style have changed
+ /// on the element, and thus whether the element has been snapshotted to do
+ /// restyle hint computation.
+ fn has_snapshot(&self) -> bool;
+
+ /// Returns whether the current snapshot if present has been handled.
+ fn handled_snapshot(&self) -> bool;
+
+ /// Flags this element as having handled already its snapshot.
+ unsafe fn set_handled_snapshot(&self);
+
+ /// Returns whether the element's styles are up-to-date after traversal
+ /// (i.e. in post traversal).
+ fn has_current_styles(&self, data: &ElementData) -> bool {
+ if self.has_snapshot() && !self.handled_snapshot() {
+ return false;
+ }
+
+ data.has_styles() &&
+ // TODO(hiro): When an animating element moved into subtree of
+ // contenteditable element, there remains animation restyle hints in
+ // post traversal. It's generally harmless since the hints will be
+ // processed in a next styling but ideally it should be processed soon.
+ //
+ // Without this, we get failures in:
+ // layout/style/crashtests/1383319.html
+ // layout/style/crashtests/1383001.html
+ //
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1389675 tracks fixing
+ // this.
+ !data.hint.has_non_animation_invalidations()
+ }
+
+ /// Flag that this element has a descendant for style processing.
+ ///
+ /// Only safe to call with exclusive access to the element.
+ unsafe fn set_dirty_descendants(&self);
+
+ /// Flag that this element has no descendant for style processing.
+ ///
+ /// Only safe to call with exclusive access to the element.
+ unsafe fn unset_dirty_descendants(&self);
+
+ /// Similar to the dirty_descendants but for representing a descendant of
+ /// the element needs to be updated in animation-only traversal.
+ fn has_animation_only_dirty_descendants(&self) -> bool {
+ false
+ }
+
+ /// Flag that this element has a descendant for animation-only restyle
+ /// processing.
+ ///
+ /// Only safe to call with exclusive access to the element.
+ unsafe fn set_animation_only_dirty_descendants(&self) {}
+
+ /// Flag that this element has no descendant for animation-only restyle processing.
+ ///
+ /// Only safe to call with exclusive access to the element.
+ unsafe fn unset_animation_only_dirty_descendants(&self) {}
+
+ /// Clear all bits related describing the dirtiness of descendants.
+ ///
+ /// In Gecko, this corresponds to the regular dirty descendants bit, the
+ /// animation-only dirty descendants bit, and the lazy frame construction
+ /// descendants bit.
+ unsafe fn clear_descendant_bits(&self) {
+ self.unset_dirty_descendants();
+ }
+
+ /// Returns true if this element is a visited link.
+ ///
+ /// Servo doesn't support visited styles yet.
+ fn is_visited_link(&self) -> bool {
+ false
+ }
+
+ /// Returns the pseudo-element implemented by this element, if any.
+ ///
+ /// Gecko traverses pseudo-elements during the style traversal, and we need
+ /// to know this so we can properly grab the pseudo-element style from the
+ /// parent element.
+ ///
+ /// Note that we still need to compute the pseudo-elements before-hand,
+ /// given otherwise we don't know if we need to create an element or not.
+ ///
+ /// Servo doesn't have to deal with this.
+ fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
+ None
+ }
+
+ /// Atomically stores the number of children of this node that we will
+ /// need to process during bottom-up traversal.
+ fn store_children_to_process(&self, n: isize);
+
+ /// Atomically notes that a child has been processed during bottom-up
+ /// traversal. Returns the number of children left to process.
+ fn did_process_child(&self) -> isize;
+
+ /// Gets a reference to the ElementData container, or creates one.
+ ///
+ /// Unsafe because it can race to allocate and leak if not used with
+ /// exclusive access to the element.
+ unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData>;
+
+ /// Clears the element data reference, if any.
+ ///
+ /// Unsafe following the same reasoning as ensure_data.
+ unsafe fn clear_data(&self);
+
+ /// Whether there is an ElementData container.
+ fn has_data(&self) -> bool;
+
+ /// Immutably borrows the ElementData.
+ fn borrow_data(&self) -> Option<AtomicRef<ElementData>>;
+
+ /// Mutably borrows the ElementData.
+ fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>>;
+
+ /// Whether we should skip any root- or item-based display property
+ /// blockification on this element. (This function exists so that Gecko
+ /// native anonymous content can opt out of this style fixup.)
+ fn skip_item_display_fixup(&self) -> bool;
+
+ /// In Gecko, element has a flag that represents the element may have
+ /// any type of animations or not to bail out animation stuff early.
+ /// Whereas Servo doesn't have such flag.
+ fn may_have_animations(&self) -> bool;
+
+ /// Creates a task to update various animation state on a given (pseudo-)element.
+ #[cfg(feature = "gecko")]
+ fn update_animations(
+ &self,
+ before_change_style: Option<Arc<ComputedValues>>,
+ tasks: UpdateAnimationsTasks,
+ );
+
+ /// Creates a task to process post animation on a given element.
+ #[cfg(feature = "gecko")]
+ fn process_post_animation(&self, tasks: PostAnimationTasks);
+
+ /// Returns true if the element has relevant animations. Relevant
+ /// animations are those animations that are affecting the element's style
+ /// or are scheduled to do so in the future.
+ fn has_animations(&self, context: &SharedStyleContext) -> bool;
+
+ /// Returns true if the element has a CSS animation. The `context` and `pseudo_element`
+ /// arguments are only used by Servo, since it stores animations globally and pseudo-elements
+ /// are not in the DOM.
+ fn has_css_animations(
+ &self,
+ context: &SharedStyleContext,
+ pseudo_element: Option<PseudoElement>,
+ ) -> bool;
+
+ /// Returns true if the element has a CSS transition (including running transitions and
+ /// completed transitions). The `context` and `pseudo_element` arguments are only used
+ /// by Servo, since it stores animations globally and pseudo-elements are not in the DOM.
+ fn has_css_transitions(
+ &self,
+ context: &SharedStyleContext,
+ pseudo_element: Option<PseudoElement>,
+ ) -> bool;
+
+ /// Returns true if the element has animation restyle hints.
+ fn has_animation_restyle_hints(&self) -> bool {
+ let data = match self.borrow_data() {
+ Some(d) => d,
+ None => return false,
+ };
+ return data.hint.has_animation_hint();
+ }
+
+ /// The shadow root this element is a host of.
+ fn shadow_root(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;
+
+ /// The shadow root which roots the subtree this element is contained in.
+ fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;
+
+ /// Return the element which we can use to look up rules in the selector
+ /// maps.
+ ///
+ /// This is always the element itself, except in the case where we are an
+ /// element-backed pseudo-element, in which case we return the originating
+ /// element.
+ fn rule_hash_target(&self) -> Self {
+ if self.is_pseudo_element() {
+ self.pseudo_element_originating_element()
+ .expect("Trying to collect rules for a detached pseudo-element")
+ } else {
+ *self
+ }
+ }
+
+ /// Executes the callback for each applicable style rule data which isn't
+ /// the main document's data (which stores UA / author rules).
+ ///
+ /// The element passed to the callback is the containing shadow host for the
+ /// data if it comes from Shadow DOM.
+ ///
+ /// Returns whether normal document author rules should apply.
+ ///
+ /// TODO(emilio): We could separate the invalidation data for elements
+ /// matching in other scopes to avoid over-invalidation.
+ fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool
+ where
+ Self: 'a,
+ F: FnMut(&'a CascadeData, Self),
+ {
+ use crate::rule_collector::containing_shadow_ignoring_svg_use;
+
+ let target = self.rule_hash_target();
+ let matches_user_and_content_rules = target.matches_user_and_content_rules();
+ let mut doc_rules_apply = matches_user_and_content_rules;
+
+ // Use the same rules to look for the containing host as we do for rule
+ // collection.
+ if let Some(shadow) = containing_shadow_ignoring_svg_use(target) {
+ doc_rules_apply = false;
+ if let Some(data) = shadow.style_data() {
+ f(data, shadow.host());
+ }
+ }
+
+ if let Some(shadow) = target.shadow_root() {
+ if let Some(data) = shadow.style_data() {
+ f(data, shadow.host());
+ }
+ }
+
+ let mut current = target.assigned_slot();
+ while let Some(slot) = current {
+ // Slots can only have assigned nodes when in a shadow tree.
+ let shadow = slot.containing_shadow().unwrap();
+ if let Some(data) = shadow.style_data() {
+ if data.any_slotted_rule() {
+ f(data, shadow.host());
+ }
+ }
+ current = slot.assigned_slot();
+ }
+
+ if target.has_part_attr() {
+ if let Some(mut inner_shadow) = target.containing_shadow() {
+ loop {
+ let inner_shadow_host = inner_shadow.host();
+ match inner_shadow_host.containing_shadow() {
+ Some(shadow) => {
+ if let Some(data) = shadow.style_data() {
+ if data.any_part_rule() {
+ f(data, shadow.host())
+ }
+ }
+ // TODO: Could be more granular.
+ if !inner_shadow_host.exports_any_part() {
+ break;
+ }
+ inner_shadow = shadow;
+ },
+ None => {
+ // TODO(emilio): Should probably distinguish with
+ // MatchesDocumentRules::{No,Yes,IfPart} or something so that we could
+ // skip some work.
+ doc_rules_apply = matches_user_and_content_rules;
+ break;
+ },
+ }
+ }
+ }
+ }
+
+ doc_rules_apply
+ }
+
+ /// Returns true if one of the transitions needs to be updated on this element. We check all
+ /// the transition properties to make sure that updating transitions is necessary.
+ /// This method should only be called if might_needs_transitions_update returns true when
+ /// passed the same parameters.
+ #[cfg(feature = "gecko")]
+ fn needs_transitions_update(
+ &self,
+ before_change_style: &ComputedValues,
+ after_change_style: &ComputedValues,
+ ) -> bool;
+
+ /// Returns the value of the `xml:lang=""` attribute (or, if appropriate,
+ /// the `lang=""` attribute) on this element.
+ fn lang_attr(&self) -> Option<AttrValue>;
+
+ /// Returns whether this element's language matches the language tag
+ /// `value`. If `override_lang` is not `None`, it specifies the value
+ /// of the `xml:lang=""` or `lang=""` attribute to use in place of
+ /// looking at the element and its ancestors. (This argument is used
+ /// to implement matching of `:lang()` against snapshots.)
+ fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool;
+
+ /// Returns whether this element is the main body element of the HTML
+ /// document it is on.
+ fn is_html_document_body_element(&self) -> bool;
+
+ /// Generate the proper applicable declarations due to presentational hints,
+ /// and insert them into `hints`.
+ fn synthesize_presentational_hints_for_legacy_attributes<V>(
+ &self,
+ visited_handling: VisitedHandlingMode,
+ hints: &mut V,
+ ) where
+ V: Push<ApplicableDeclarationBlock>;
+
+ /// Returns element's local name.
+ fn local_name(&self) -> &<SelectorImpl as selectors::parser::SelectorImpl>::BorrowedLocalName;
+
+ /// Returns element's namespace.
+ fn namespace(&self)
+ -> &<SelectorImpl as selectors::parser::SelectorImpl>::BorrowedNamespaceUrl;
+
+ /// Returns the size of the element to be used in container size queries.
+ /// This will usually be the size of the content area of the primary box,
+ /// but can be None if there is no box or if some axis lacks size containment.
+ fn query_container_size(
+ &self,
+ display: &Display,
+ ) -> euclid::default::Size2D<Option<app_units::Au>>;
+
+ /// Returns true if this element anchors a relative selector, now or after
+ /// a DOM mutation.
+ fn anchors_relative_selector(&self) -> bool;
+}
+
+/// TNode and TElement aren't Send because we want to be careful and explicit
+/// about our parallel traversal. However, there are certain situations
+/// (including but not limited to the traversal) where we need to send DOM
+/// objects to other threads.
+///
+/// That's the reason why `SendNode` exists.
+#[derive(Clone, Debug, PartialEq)]
+pub struct SendNode<N: TNode>(N);
+unsafe impl<N: TNode> Send for SendNode<N> {}
+impl<N: TNode> SendNode<N> {
+ /// Unsafely construct a SendNode.
+ pub unsafe fn new(node: N) -> Self {
+ SendNode(node)
+ }
+}
+impl<N: TNode> Deref for SendNode<N> {
+ type Target = N;
+ fn deref(&self) -> &N {
+ &self.0
+ }
+}
+
+/// Same reason as for the existence of SendNode, SendElement does the proper
+/// things for a given `TElement`.
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct SendElement<E: TElement>(E);
+unsafe impl<E: TElement> Send for SendElement<E> {}
+impl<E: TElement> SendElement<E> {
+ /// Unsafely construct a SendElement.
+ pub unsafe fn new(el: E) -> Self {
+ SendElement(el)
+ }
+}
+impl<E: TElement> Deref for SendElement<E> {
+ type Target = E;
+ fn deref(&self) -> &E {
+ &self.0
+ }
+}