aboutsummaryrefslogtreecommitdiffstats
path: root/components/style/gecko/wrapper.rs
diff options
context:
space:
mode:
authorDelan Azabani <dazabani@igalia.com>2024-02-27 23:39:06 +0800
committerGitHub <noreply@github.com>2024-02-27 15:39:06 +0000
commitfaf754dfa655f0b9a28f62bc47a78fbf78ebcaf4 (patch)
tree4725e1446680d036797b1fc258733ae6b2c9f354 /components/style/gecko/wrapper.rs
parentb07505417e629bbb081be9683630f2d7a5f50544 (diff)
downloadservo-faf754dfa655f0b9a28f62bc47a78fbf78ebcaf4.tar.gz
servo-faf754dfa655f0b9a28f62bc47a78fbf78ebcaf4.zip
Move Stylo to its own repo (#31350)
* Remove packages that were moved to external repo * Add workspace dependencies pointing to 2023-06-14 branch * Fix servo-tidy.toml errors * Update commit to include #31346 * Update commit to include servo/stylo#2 * Move css-properties.json lookup to target/doc/stylo * Remove dependency on vendored mako in favour of pypi dependency This also removes etc/ci/generate_workflow.py, which has been unused since at least 9e71bd6a7010d6e5723831696ae0ebe26b47682f. * Add temporary code to debug Windows test failures * Fix failures on Windows due to custom target dir * Update commit to include servo/stylo#3 * Fix license in tests/unit/style/build.rs * Document how to build with local Stylo in Cargo.toml
Diffstat (limited to 'components/style/gecko/wrapper.rs')
-rw-r--r--components/style/gecko/wrapper.rs2125
1 files changed, 0 insertions, 2125 deletions
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
deleted file mode 100644
index 490c063fbe7..00000000000
--- a/components/style/gecko/wrapper.rs
+++ /dev/null
@@ -1,2125 +0,0 @@
-/* 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/. */
-
-#![allow(unsafe_code)]
-
-//! Wrapper definitions on top of Gecko types in order to be used in the style
-//! system.
-//!
-//! This really follows the Servo pattern in
-//! `components/script/layout_wrapper.rs`.
-//!
-//! This theoretically should live in its own crate, but now it lives in the
-//! style system it's kind of pointless in the Stylo case, and only Servo forces
-//! the separation between the style system implementation and everything else.
-
-use crate::applicable_declarations::ApplicableDeclarationBlock;
-use crate::context::{PostAnimationTasks, QuirksMode, SharedStyleContext, UpdateAnimationsTasks};
-use crate::data::ElementData;
-use crate::dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot};
-use crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};
-use crate::gecko::snapshot_helpers;
-use crate::gecko_bindings::bindings;
-use crate::gecko_bindings::bindings::Gecko_ElementHasAnimations;
-use crate::gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
-use crate::gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
-use crate::gecko_bindings::bindings::Gecko_ElementState;
-use crate::gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_GetAnimationEffectCount;
-use crate::gecko_bindings::bindings::Gecko_GetAnimationRule;
-use crate::gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;
-use crate::gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock;
-use crate::gecko_bindings::bindings::Gecko_IsSignificantChild;
-use crate::gecko_bindings::bindings::Gecko_MatchLang;
-use crate::gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr;
-use crate::gecko_bindings::bindings::Gecko_UpdateAnimations;
-use crate::gecko_bindings::structs;
-use crate::gecko_bindings::structs::nsChangeHint;
-use crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
-use crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
-use crate::gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
-use crate::gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
-use crate::gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
-use crate::gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES;
-use crate::gecko_bindings::structs::NODE_NEEDS_FRAME;
-use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag};
-use crate::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement};
-use crate::global_style_data::GLOBAL_STYLE_DATA;
-use crate::invalidation::element::restyle_hints::RestyleHint;
-use crate::media_queries::Device;
-use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
-use crate::properties::{ComputedValues, LonghandId};
-use crate::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
-use crate::rule_tree::CascadeLevel as ServoCascadeLevel;
-use crate::selector_parser::{AttrValue, Lang};
-use crate::shared_lock::{Locked, SharedRwLock};
-use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
-use crate::stylist::CascadeData;
-use crate::values::computed::Display;
-use crate::values::{AtomIdent, AtomString};
-use crate::CaseSensitivityExt;
-use crate::LocalName;
-use app_units::Au;
-use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
-use dom::{DocumentState, ElementState};
-use euclid::default::Size2D;
-use fxhash::FxHashMap;
-use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
-use selectors::matching::VisitedHandlingMode;
-use selectors::matching::{ElementSelectorFlags, MatchingContext};
-use selectors::sink::Push;
-use selectors::{Element, OpaqueElement};
-use servo_arc::{Arc, ArcBorrow};
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use std::mem;
-use std::ptr;
-use std::sync::atomic::{AtomicU32, Ordering};
-
-#[inline]
-fn elements_with_id<'a, 'le>(
- array: *const structs::nsTArray<*mut RawGeckoElement>,
-) -> &'a [GeckoElement<'le>] {
- unsafe {
- if array.is_null() {
- return &[];
- }
-
- let elements: &[*mut RawGeckoElement] = &**array;
-
- // NOTE(emilio): We rely on the in-memory representation of
- // GeckoElement<'ld> and *mut RawGeckoElement being the same.
- #[allow(dead_code)]
- unsafe fn static_assert() {
- mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _);
- }
-
- mem::transmute(elements)
- }
-}
-
-/// A simple wrapper over `Document`.
-#[derive(Clone, Copy)]
-pub struct GeckoDocument<'ld>(pub &'ld structs::Document);
-
-impl<'ld> TDocument for GeckoDocument<'ld> {
- type ConcreteNode = GeckoNode<'ld>;
-
- #[inline]
- fn as_node(&self) -> Self::ConcreteNode {
- GeckoNode(&self.0._base)
- }
-
- #[inline]
- fn is_html_document(&self) -> bool {
- self.0.mType == structs::Document_Type::eHTML
- }
-
- #[inline]
- fn quirks_mode(&self) -> QuirksMode {
- self.0.mCompatMode.into()
- }
-
- #[inline]
- fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'ld>], ()>
- where
- Self: 'a,
- {
- Ok(elements_with_id(unsafe {
- bindings::Gecko_Document_GetElementsWithId(self.0, id.as_ptr())
- }))
- }
-
- fn shared_lock(&self) -> &SharedRwLock {
- &GLOBAL_STYLE_DATA.shared_lock
- }
-}
-
-/// A simple wrapper over `ShadowRoot`.
-#[derive(Clone, Copy)]
-pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
-
-impl<'ln> fmt::Debug for GeckoShadowRoot<'ln> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- // TODO(emilio): Maybe print the host or something?
- write!(f, "<shadow-root> ({:#x})", self.as_node().opaque().0)
- }
-}
-
-impl<'lr> PartialEq for GeckoShadowRoot<'lr> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.0 as *const _ == other.0 as *const _
- }
-}
-
-impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
- type ConcreteNode = GeckoNode<'lr>;
-
- #[inline]
- fn as_node(&self) -> Self::ConcreteNode {
- GeckoNode(&self.0._base._base._base._base)
- }
-
- #[inline]
- fn host(&self) -> GeckoElement<'lr> {
- GeckoElement(unsafe { &*self.0._base.mHost.mRawPtr })
- }
-
- #[inline]
- fn style_data<'a>(&self) -> Option<&'a CascadeData>
- where
- Self: 'a,
- {
- let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? };
- Some(&author_styles.data)
- }
-
- #[inline]
- fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'lr>], ()>
- where
- Self: 'a,
- {
- Ok(elements_with_id(unsafe {
- bindings::Gecko_ShadowRoot_GetElementsWithId(self.0, id.as_ptr())
- }))
- }
-
- #[inline]
- fn parts<'a>(&self) -> &[<Self::ConcreteNode as TNode>::ConcreteElement]
- where
- Self: 'a,
- {
- let slice: &[*const RawGeckoElement] = &*self.0.mParts;
-
- #[allow(dead_code)]
- unsafe fn static_assert() {
- mem::transmute::<*const RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *const _);
- }
-
- unsafe { mem::transmute(slice) }
- }
-}
-
-/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
-///
-/// Important: We don't currently refcount the DOM, because the wrapper lifetime
-/// magic guarantees that our LayoutFoo references won't outlive the root, and
-/// we don't mutate any of the references on the Gecko side during restyle.
-///
-/// We could implement refcounting if need be (at a potentially non-trivial
-/// performance cost) by implementing Drop and making LayoutFoo non-Copy.
-#[derive(Clone, Copy)]
-pub struct GeckoNode<'ln>(pub &'ln RawGeckoNode);
-
-impl<'ln> PartialEq for GeckoNode<'ln> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.0 as *const _ == other.0 as *const _
- }
-}
-
-impl<'ln> fmt::Debug for GeckoNode<'ln> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if let Some(el) = self.as_element() {
- return el.fmt(f);
- }
-
- if self.is_text_node() {
- return write!(f, "<text node> ({:#x})", self.opaque().0);
- }
-
- if self.is_document() {
- return write!(f, "<document> ({:#x})", self.opaque().0);
- }
-
- if let Some(sr) = self.as_shadow_root() {
- return sr.fmt(f);
- }
-
- write!(f, "<non-text node> ({:#x})", self.opaque().0)
- }
-}
-
-impl<'ln> GeckoNode<'ln> {
- #[inline]
- fn is_document(&self) -> bool {
- // This is a DOM constant that isn't going to change.
- const DOCUMENT_NODE: u16 = 9;
- self.node_info().mInner.mNodeType == DOCUMENT_NODE
- }
-
- #[inline]
- fn is_shadow_root(&self) -> bool {
- self.is_in_shadow_tree() && self.parent_node().is_none()
- }
-
- #[inline]
- fn from_content(content: &'ln nsIContent) -> Self {
- GeckoNode(&content._base)
- }
-
- #[inline]
- fn set_flags(&self, flags: u32) {
- self.flags_atomic().fetch_or(flags, Ordering::Relaxed);
- }
-
- #[inline]
- fn flags_atomic(&self) -> &AtomicU32 {
- use std::cell::Cell;
- let flags: &Cell<u32> = &(self.0)._base._base_1.mFlags;
-
- #[allow(dead_code)]
- fn static_assert() {
- let _: [u8; std::mem::size_of::<Cell<u32>>()] = [0u8; std::mem::size_of::<AtomicU32>()];
- let _: [u8; std::mem::align_of::<Cell<u32>>()] =
- [0u8; std::mem::align_of::<AtomicU32>()];
- }
-
- // Rust doesn't provide standalone atomic functions like GCC/clang do
- // (via the atomic intrinsics) or via std::atomic_ref, but it guarantees
- // that the memory representation of u32 and AtomicU32 matches:
- // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicU32.html
- unsafe { std::mem::transmute::<&Cell<u32>, &AtomicU32>(flags) }
- }
-
- #[inline]
- fn flags(&self) -> u32 {
- self.flags_atomic().load(Ordering::Relaxed)
- }
-
- #[inline]
- fn node_info(&self) -> &structs::NodeInfo {
- debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null());
- unsafe { &*self.0.mNodeInfo.mRawPtr }
- }
-
- // These live in different locations depending on processor architecture.
- #[cfg(target_pointer_width = "64")]
- #[inline]
- fn bool_flags(&self) -> u32 {
- (self.0)._base._base_1.mBoolFlags
- }
-
- #[cfg(target_pointer_width = "32")]
- #[inline]
- fn bool_flags(&self) -> u32 {
- (self.0).mBoolFlags
- }
-
- #[inline]
- fn get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool {
- self.bool_flags() & (1u32 << flag as u32) != 0
- }
-
- /// This logic is duplicate in Gecko's nsINode::IsInShadowTree().
- #[inline]
- fn is_in_shadow_tree(&self) -> bool {
- use crate::gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;
- self.flags() & NODE_IS_IN_SHADOW_TREE != 0
- }
-
- /// Returns true if we know for sure that `flattened_tree_parent` and `parent_node` return the
- /// same thing.
- ///
- /// TODO(emilio): Measure and consider not doing this fast-path, it's only a function call and
- /// from profiles it seems that keeping this fast path makes the compiler not inline
- /// `flattened_tree_parent` as a whole, so we're not gaining much either.
- #[inline]
- fn flattened_tree_parent_is_parent(&self) -> bool {
- use crate::gecko_bindings::structs::*;
- let flags = self.flags();
-
- let parent = match self.parent_node() {
- Some(p) => p,
- None => return true,
- };
-
- if parent.is_shadow_root() {
- return false;
- }
-
- if let Some(parent) = parent.as_element() {
- if flags & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0 && parent.is_root() {
- return false;
- }
- if parent.shadow_root().is_some() || parent.is_html_slot_element() {
- return false;
- }
- }
-
- true
- }
-
- #[inline]
- fn flattened_tree_parent(&self) -> Option<Self> {
- if self.flattened_tree_parent_is_parent() {
- debug_assert_eq!(
- unsafe {
- bindings::Gecko_GetFlattenedTreeParentNode(self.0)
- .as_ref()
- .map(GeckoNode)
- },
- self.parent_node(),
- "Fast path stopped holding!"
- );
- return self.parent_node();
- }
-
- // NOTE(emilio): If this call is too expensive, we could manually inline more aggressively.
- unsafe {
- bindings::Gecko_GetFlattenedTreeParentNode(self.0)
- .as_ref()
- .map(GeckoNode)
- }
- }
-
- #[inline]
- fn contains_non_whitespace_content(&self) -> bool {
- unsafe { Gecko_IsSignificantChild(self.0, false) }
- }
-}
-
-impl<'ln> NodeInfo for GeckoNode<'ln> {
- #[inline]
- fn is_element(&self) -> bool {
- self.get_bool_flag(nsINode_BooleanFlag::NodeIsElement)
- }
-
- fn is_text_node(&self) -> bool {
- // This is a DOM constant that isn't going to change.
- const TEXT_NODE: u16 = 3;
- self.node_info().mInner.mNodeType == TEXT_NODE
- }
-}
-
-impl<'ln> TNode for GeckoNode<'ln> {
- type ConcreteDocument = GeckoDocument<'ln>;
- type ConcreteShadowRoot = GeckoShadowRoot<'ln>;
- type ConcreteElement = GeckoElement<'ln>;
-
- #[inline]
- fn parent_node(&self) -> Option<Self> {
- unsafe { self.0.mParent.as_ref().map(GeckoNode) }
- }
-
- #[inline]
- fn first_child(&self) -> Option<Self> {
- unsafe {
- self.0
- .mFirstChild
- .raw()
- .as_ref()
- .map(GeckoNode::from_content)
- }
- }
-
- #[inline]
- fn last_child(&self) -> Option<Self> {
- unsafe { bindings::Gecko_GetLastChild(self.0).as_ref().map(GeckoNode) }
- }
-
- #[inline]
- fn prev_sibling(&self) -> Option<Self> {
- unsafe {
- let prev_or_last = GeckoNode::from_content(self.0.mPreviousOrLastSibling.as_ref()?);
- if prev_or_last.0.mNextSibling.raw().is_null() {
- return None;
- }
- Some(prev_or_last)
- }
- }
-
- #[inline]
- fn next_sibling(&self) -> Option<Self> {
- unsafe {
- self.0
- .mNextSibling
- .raw()
- .as_ref()
- .map(GeckoNode::from_content)
- }
- }
-
- #[inline]
- fn owner_doc(&self) -> Self::ConcreteDocument {
- debug_assert!(!self.node_info().mDocument.is_null());
- GeckoDocument(unsafe { &*self.node_info().mDocument })
- }
-
- #[inline]
- fn is_in_document(&self) -> bool {
- self.get_bool_flag(nsINode_BooleanFlag::IsInDocument)
- }
-
- fn traversal_parent(&self) -> Option<GeckoElement<'ln>> {
- self.flattened_tree_parent().and_then(|n| n.as_element())
- }
-
- #[inline]
- fn opaque(&self) -> OpaqueNode {
- let ptr: usize = self.0 as *const _ as usize;
- OpaqueNode(ptr)
- }
-
- fn debug_id(self) -> usize {
- unimplemented!()
- }
-
- #[inline]
- fn as_element(&self) -> Option<GeckoElement<'ln>> {
- if !self.is_element() {
- return None;
- }
-
- Some(GeckoElement(unsafe {
- &*(self.0 as *const _ as *const RawGeckoElement)
- }))
- }
-
- #[inline]
- fn as_document(&self) -> Option<Self::ConcreteDocument> {
- if !self.is_document() {
- return None;
- }
-
- debug_assert_eq!(self.owner_doc().as_node(), *self, "How?");
- Some(self.owner_doc())
- }
-
- #[inline]
- fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {
- if !self.is_shadow_root() {
- return None;
- }
-
- Some(GeckoShadowRoot(unsafe {
- &*(self.0 as *const _ as *const structs::ShadowRoot)
- }))
- }
-}
-
-/// A wrapper on top of two kind of iterators, depending on the parent being
-/// iterated.
-///
-/// We generally iterate children by traversing the light-tree siblings of the
-/// first child like Servo does.
-///
-/// However, for nodes with anonymous children, we use a custom (heavier-weight)
-/// Gecko-implemented iterator.
-///
-/// FIXME(emilio): If we take into account shadow DOM, we're going to need the
-/// flat tree pretty much always. We can try to optimize the case where there's
-/// no shadow root sibling, probably.
-pub enum GeckoChildrenIterator<'a> {
- /// A simple iterator that tracks the current node being iterated and
- /// replaces it with the next sibling when requested.
- Current(Option<GeckoNode<'a>>),
- /// A Gecko-implemented iterator we need to drop appropriately.
- GeckoIterator(structs::StyleChildrenIterator),
-}
-
-impl<'a> Drop for GeckoChildrenIterator<'a> {
- fn drop(&mut self) {
- if let GeckoChildrenIterator::GeckoIterator(ref mut it) = *self {
- unsafe {
- bindings::Gecko_DestroyStyleChildrenIterator(it);
- }
- }
- }
-}
-
-impl<'a> Iterator for GeckoChildrenIterator<'a> {
- type Item = GeckoNode<'a>;
- fn next(&mut self) -> Option<GeckoNode<'a>> {
- match *self {
- GeckoChildrenIterator::Current(curr) => {
- let next = curr.and_then(|node| node.next_sibling());
- *self = GeckoChildrenIterator::Current(next);
- curr
- },
- GeckoChildrenIterator::GeckoIterator(ref mut it) => unsafe {
- // We do this unsafe lengthening of the lifetime here because
- // structs::StyleChildrenIterator is actually StyleChildrenIterator<'a>,
- // however we can't express this easily with bindgen, and it would
- // introduce functions with two input lifetimes into bindgen,
- // which would be out of scope for elision.
- bindings::Gecko_GetNextStyleChild(&mut *(it as *mut _))
- .as_ref()
- .map(GeckoNode)
- },
- }
- }
-}
-
-/// A simple wrapper over a non-null Gecko `Element` pointer.
-#[derive(Clone, Copy)]
-pub struct GeckoElement<'le>(pub &'le RawGeckoElement);
-
-impl<'le> fmt::Debug for GeckoElement<'le> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use nsstring::nsCString;
-
- write!(f, "<{}", self.local_name())?;
-
- let mut attrs = nsCString::new();
- unsafe {
- bindings::Gecko_Element_DebugListAttributes(self.0, &mut attrs);
- }
- write!(f, "{}", attrs)?;
- write!(f, "> ({:#x})", self.as_node().opaque().0)
- }
-}
-
-impl<'le> GeckoElement<'le> {
- /// Gets the raw `ElementData` refcell for the element.
- #[inline(always)]
- pub fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
- unsafe { self.0.mServoData.get().as_ref() }
- }
-
- /// Returns whether any animation applies to this element.
- #[inline]
- pub fn has_any_animation(&self) -> bool {
- self.may_have_animations() && unsafe { Gecko_ElementHasAnimations(self.0) }
- }
-
- #[inline(always)]
- fn attrs(&self) -> Option<&structs::AttrArray_Impl> {
- unsafe { self.0.mAttrs.mImpl.mPtr.as_ref() }
- }
-
- #[inline(always)]
- fn non_mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] {
- let attrs = match self.attrs() {
- Some(attrs) => attrs,
- None => return &[],
- };
- unsafe {
- attrs.mBuffer.as_slice(attrs.mAttrCount as usize)
- }
- }
-
- #[inline(always)]
- fn mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] {
- let attrs = match self.attrs() {
- Some(attrs) => attrs,
- None => return &[],
- };
- unsafe {
- let attrs = match attrs.mMappedAttrs.as_ref() {
- Some(attrs) => attrs,
- None => return &[],
- };
-
- attrs.mBuffer.as_slice(attrs.mAttrCount as usize)
- }
- }
-
- #[inline]
- fn iter_attrs(&self) -> impl Iterator<Item = &structs::AttrArray_InternalAttr> {
- self.non_mapped_attrs().iter().chain(self.mapped_attrs().iter())
- }
-
- #[inline(always)]
- fn get_part_attr(&self) -> Option<&structs::nsAttrValue> {
- if !self.has_part_attr() {
- return None;
- }
- snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("part"))
- }
-
- #[inline(always)]
- fn get_class_attr(&self) -> Option<&structs::nsAttrValue> {
- if !self.may_have_class() {
- return None;
- }
-
- if self.is_svg_element() {
- let svg_class = unsafe { bindings::Gecko_GetSVGAnimatedClass(self.0).as_ref() };
- if let Some(c) = svg_class {
- return Some(c);
- }
- }
-
- snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class"))
- }
-
- #[inline]
- fn may_have_anonymous_children(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveAnonymousChildren)
- }
-
- #[inline]
- fn flags(&self) -> u32 {
- self.as_node().flags()
- }
-
- #[inline]
- fn set_flags(&self, flags: u32) {
- self.as_node().set_flags(flags);
- }
-
- #[inline]
- unsafe fn unset_flags(&self, flags: u32) {
- self.as_node()
- .flags_atomic()
- .fetch_and(!flags, Ordering::Relaxed);
- }
-
- /// Returns true if this element has descendants for lazy frame construction.
- #[inline]
- pub fn descendants_need_frames(&self) -> bool {
- self.flags() & NODE_DESCENDANTS_NEED_FRAMES != 0
- }
-
- /// Returns true if this element needs lazy frame construction.
- #[inline]
- pub fn needs_frame(&self) -> bool {
- self.flags() & NODE_NEEDS_FRAME != 0
- }
-
- /// Returns a reference to the DOM slots for this Element, if they exist.
- #[inline]
- fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> {
- let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots;
- unsafe { slots.as_ref() }
- }
-
- /// Returns a reference to the extended DOM slots for this Element.
- #[inline]
- fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {
- self.dom_slots().and_then(|s| unsafe {
- // For the bit usage, see nsContentSlots::GetExtendedSlots.
- let e_slots = s._base.mExtendedSlots &
- !structs::nsIContent_nsContentSlots_sNonOwningExtendedSlotsFlag;
- (e_slots as *const structs::FragmentOrElement_nsExtendedDOMSlots).as_ref()
- })
- }
-
- #[inline]
- fn namespace_id(&self) -> i32 {
- self.as_node().node_info().mInner.mNamespaceID
- }
-
- #[inline]
- fn has_id(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasID)
- }
-
- #[inline]
- fn state_internal(&self) -> u64 {
- if !self
- .as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasLockedStyleStates)
- {
- return self.0.mState.bits;
- }
- unsafe { Gecko_ElementState(self.0) }
- }
-
- #[inline]
- fn document_state(&self) -> DocumentState {
- DocumentState::from_bits_truncate(self.as_node().owner_doc().0.mDocumentState.bits)
- }
-
- #[inline]
- fn may_have_class(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveClass)
- }
-
- #[inline]
- fn has_properties(&self) -> bool {
- use crate::gecko_bindings::structs::NODE_HAS_PROPERTIES;
-
- self.flags() & NODE_HAS_PROPERTIES != 0
- }
-
- #[inline]
- fn before_or_after_pseudo(&self, is_before: bool) -> Option<Self> {
- if !self.has_properties() {
- return None;
- }
-
- unsafe {
- bindings::Gecko_GetBeforeOrAfterPseudo(self.0, is_before)
- .as_ref()
- .map(GeckoElement)
- }
- }
-
- #[inline]
- fn may_have_style_attribute(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveStyle)
- }
-
- /// Only safe to call on the main thread, with exclusive access to the
- /// element and its ancestors.
- ///
- /// This function is also called after display property changed for SMIL
- /// animation.
- ///
- /// Also this function schedules style flush.
- pub unsafe fn note_explicit_hints(&self, restyle_hint: RestyleHint, change_hint: nsChangeHint) {
- use crate::gecko::restyle_damage::GeckoRestyleDamage;
-
- let damage = GeckoRestyleDamage::new(change_hint);
- debug!(
- "note_explicit_hints: {:?}, restyle_hint={:?}, change_hint={:?}",
- self, restyle_hint, change_hint
- );
-
- debug_assert!(
- !(restyle_hint.has_animation_hint() && restyle_hint.has_non_animation_hint()),
- "Animation restyle hints should not appear with non-animation restyle hints"
- );
-
- let mut data = match self.mutate_data() {
- Some(d) => d,
- None => {
- debug!("(Element not styled, discarding hints)");
- return;
- },
- };
-
- debug_assert!(data.has_styles(), "how?");
-
- // Propagate the bit up the chain.
- if restyle_hint.has_animation_hint() {
- bindings::Gecko_NoteAnimationOnlyDirtyElement(self.0);
- } else {
- bindings::Gecko_NoteDirtyElement(self.0);
- }
-
- data.hint.insert(restyle_hint);
- data.damage |= damage;
- }
-
- /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree.
- #[inline]
- fn is_root_of_native_anonymous_subtree(&self) -> bool {
- use crate::gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT;
- return self.flags() & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0;
- }
-
- /// Returns true if this node is the shadow root of an use-element shadow tree.
- #[inline]
- fn is_root_of_use_element_shadow_tree(&self) -> bool {
- if !self.as_node().is_in_shadow_tree() {
- return false;
- }
- if !self.parent_node_is_shadow_root() {
- return false;
- }
- let host = self.containing_shadow_host().unwrap();
- host.is_svg_element() && host.local_name() == &**local_name!("use")
- }
-
- fn css_transitions_info(&self) -> FxHashMap<LonghandId, Arc<AnimationValue>> {
- use crate::gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
- use crate::gecko_bindings::bindings::Gecko_ElementTransitions_Length;
-
- let collection_length = unsafe { Gecko_ElementTransitions_Length(self.0) } as usize;
- let mut map = FxHashMap::with_capacity_and_hasher(collection_length, Default::default());
-
- for i in 0..collection_length {
- let end_value =
- unsafe { Arc::from_raw_addrefed(Gecko_ElementTransitions_EndValueAt(self.0, i)) };
- let property = end_value.id();
- debug_assert!(!property.is_logical());
- map.insert(property, end_value);
- }
- map
- }
-
- fn needs_transitions_update_per_property(
- &self,
- longhand_id: LonghandId,
- combined_duration_seconds: f32,
- before_change_style: &ComputedValues,
- after_change_style: &ComputedValues,
- existing_transitions: &FxHashMap<LonghandId, Arc<AnimationValue>>,
- ) -> bool {
- use crate::values::animated::{Animate, Procedure};
- debug_assert!(!longhand_id.is_logical());
-
- // If there is an existing transition, update only if the end value
- // differs.
- //
- // If the end value has not changed, we should leave the currently
- // running transition as-is since we don't want to interrupt its timing
- // function.
- if let Some(ref existing) = existing_transitions.get(&longhand_id) {
- let after_value =
- AnimationValue::from_computed_values(longhand_id, after_change_style).unwrap();
-
- return ***existing != after_value;
- }
-
- let from = AnimationValue::from_computed_values(longhand_id, before_change_style);
- let to = AnimationValue::from_computed_values(longhand_id, after_change_style);
-
- debug_assert_eq!(to.is_some(), from.is_some());
-
- combined_duration_seconds > 0.0f32 &&
- from != to &&
- from.unwrap()
- .animate(
- to.as_ref().unwrap(),
- Procedure::Interpolate { progress: 0.5 },
- )
- .is_ok()
- }
-}
-
-/// Converts flags from the layout used by rust-selectors to the layout used
-/// by Gecko. We could align these and then do this without conditionals, but
-/// it's probably not worth the trouble.
-fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
- use crate::gecko_bindings::structs::*;
- let mut gecko_flags = 0u32;
- if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
- gecko_flags |= NODE_HAS_SLOW_SELECTOR;
- }
- if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
- gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS;
- }
- if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) {
- gecko_flags |= NODE_HAS_SLOW_SELECTOR_NTH_OF;
- }
- if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
- gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR;
- }
- if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) {
- gecko_flags |= NODE_HAS_EMPTY_SELECTOR;
- }
-
- gecko_flags
-}
-
-fn get_animation_rule(
- element: &GeckoElement,
- cascade_level: CascadeLevel,
-) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
- use crate::properties::longhands::ANIMATABLE_PROPERTY_COUNT;
-
- // There's a very rough correlation between the number of effects
- // (animations) on an element and the number of properties it is likely to
- // animate, so we use that as an initial guess for the size of the
- // AnimationValueMap in order to reduce the number of re-allocations needed.
- let effect_count = unsafe { Gecko_GetAnimationEffectCount(element.0) };
- // Also, we should try to reuse the PDB, to avoid creating extra rule nodes.
- let mut animation_values = AnimationValueMap::with_capacity_and_hasher(
- effect_count.min(ANIMATABLE_PROPERTY_COUNT),
- Default::default(),
- );
- if unsafe { Gecko_GetAnimationRule(element.0, cascade_level, &mut animation_values) } {
- let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
- Some(Arc::new(shared_lock.wrap(
- PropertyDeclarationBlock::from_animation_value_map(&animation_values),
- )))
- } else {
- None
- }
-}
-
-/// Turns a gecko namespace id into an atom. Might panic if you pass any random thing that isn't a
-/// namespace id.
-#[inline(always)]
-pub unsafe fn namespace_id_to_atom(id: i32) -> *mut nsAtom {
- unsafe {
- let namespace_manager = structs::nsNameSpaceManager_sInstance.mRawPtr;
- (*namespace_manager).mURIArray[id as usize].mRawPtr
- }
-}
-
-impl<'le> TElement for GeckoElement<'le> {
- type ConcreteNode = GeckoNode<'le>;
- type TraversalChildrenIterator = GeckoChildrenIterator<'le>;
-
- fn inheritance_parent(&self) -> Option<Self> {
- if self.is_pseudo_element() {
- return self.pseudo_element_originating_element();
- }
-
- self.as_node()
- .flattened_tree_parent()
- .and_then(|n| n.as_element())
- }
-
- fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {
- // This condition is similar to the check that
- // StyleChildrenIterator::IsNeeded does, except that it might return
- // true if we used to (but no longer) have anonymous content from
- // ::before/::after, or nsIAnonymousContentCreators.
- if self.is_html_slot_element() ||
- self.shadow_root().is_some() ||
- self.may_have_anonymous_children()
- {
- unsafe {
- let mut iter: structs::StyleChildrenIterator = ::std::mem::zeroed();
- bindings::Gecko_ConstructStyleChildrenIterator(self.0, &mut iter);
- return LayoutIterator(GeckoChildrenIterator::GeckoIterator(iter));
- }
- }
-
- LayoutIterator(GeckoChildrenIterator::Current(self.as_node().first_child()))
- }
-
- fn before_pseudo_element(&self) -> Option<Self> {
- self.before_or_after_pseudo(/* is_before = */ true)
- }
-
- fn after_pseudo_element(&self) -> Option<Self> {
- self.before_or_after_pseudo(/* is_before = */ false)
- }
-
- fn marker_pseudo_element(&self) -> Option<Self> {
- if !self.has_properties() {
- return None;
- }
-
- unsafe {
- bindings::Gecko_GetMarkerPseudo(self.0)
- .as_ref()
- .map(GeckoElement)
- }
- }
-
- #[inline]
- fn is_html_element(&self) -> bool {
- self.namespace_id() == structs::kNameSpaceID_XHTML as i32
- }
-
- #[inline]
- fn is_mathml_element(&self) -> bool {
- self.namespace_id() == structs::kNameSpaceID_MathML as i32
- }
-
- #[inline]
- fn is_svg_element(&self) -> bool {
- self.namespace_id() == structs::kNameSpaceID_SVG as i32
- }
-
- #[inline]
- fn is_xul_element(&self) -> bool {
- self.namespace_id() == structs::root::kNameSpaceID_XUL as i32
- }
-
- #[inline]
- fn local_name(&self) -> &WeakAtom {
- unsafe { WeakAtom::new(self.as_node().node_info().mInner.mName) }
- }
-
- #[inline]
- fn namespace(&self) -> &WeakNamespace {
- unsafe { WeakNamespace::new(namespace_id_to_atom(self.namespace_id())) }
- }
-
- #[inline]
- fn query_container_size(&self, display: &Display) -> Size2D<Option<Au>> {
- // If an element gets 'display: contents' and its nsIFrame has not been removed yet,
- // Gecko_GetQueryContainerSize will not notice that it can't have size containment.
- // Other cases like 'display: inline' will be handled once the new nsIFrame is created.
- if display.is_contents() {
- return Size2D::new(None, None);
- }
-
- let mut width = -1;
- let mut height = -1;
- unsafe {
- bindings::Gecko_GetQueryContainerSize(self.0, &mut width, &mut height);
- }
- Size2D::new(
- if width >= 0 { Some(Au(width)) } else { None },
- if height >= 0 { Some(Au(height)) } else { None },
- )
- }
-
- /// Return the list of slotted nodes of this node.
- #[inline]
- fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
- if !self.is_html_slot_element() || !self.as_node().is_in_shadow_tree() {
- return &[];
- }
-
- let slot: &structs::HTMLSlotElement = unsafe { mem::transmute(self.0) };
-
- if cfg!(debug_assertions) {
- let base: &RawGeckoElement = &slot._base._base._base._base;
- assert_eq!(base as *const _, self.0 as *const _, "Bad cast");
- }
-
- // FIXME(emilio): Workaround a bindgen bug on Android that causes
- // mAssignedNodes to be at the wrong offset. See bug 1466406.
- //
- // Bug 1466580 tracks running the Android layout tests on automation.
- //
- // The actual bindgen bug still needs reduction.
- let assigned_nodes: &[structs::RefPtr<structs::nsINode>] = if !cfg!(target_os = "android") {
- debug_assert_eq!(
- unsafe { bindings::Gecko_GetAssignedNodes(self.0) },
- &slot.mAssignedNodes as *const _,
- );
-
- &*slot.mAssignedNodes
- } else {
- unsafe { &**bindings::Gecko_GetAssignedNodes(self.0) }
- };
-
- debug_assert_eq!(
- mem::size_of::<structs::RefPtr<structs::nsINode>>(),
- mem::size_of::<Self::ConcreteNode>(),
- "Bad cast!"
- );
-
- unsafe { mem::transmute(assigned_nodes) }
- }
-
- #[inline]
- fn shadow_root(&self) -> Option<GeckoShadowRoot<'le>> {
- let slots = self.extended_slots()?;
- unsafe { slots.mShadowRoot.mRawPtr.as_ref().map(GeckoShadowRoot) }
- }
-
- #[inline]
- fn containing_shadow(&self) -> Option<GeckoShadowRoot<'le>> {
- let slots = self.extended_slots()?;
- unsafe {
- slots
- ._base
- .mContainingShadow
- .mRawPtr
- .as_ref()
- .map(GeckoShadowRoot)
- }
- }
-
- fn each_anonymous_content_child<F>(&self, mut f: F)
- where
- F: FnMut(Self),
- {
- if !self.may_have_anonymous_children() {
- return;
- }
-
- let array: *mut structs::nsTArray<*mut nsIContent> =
- unsafe { bindings::Gecko_GetAnonymousContentForElement(self.0) };
-
- if array.is_null() {
- return;
- }
-
- for content in unsafe { &**array } {
- let node = GeckoNode::from_content(unsafe { &**content });
- let element = match node.as_element() {
- Some(e) => e,
- None => continue,
- };
-
- f(element);
- }
-
- unsafe { bindings::Gecko_DestroyAnonymousContentList(array) };
- }
-
- #[inline]
- fn as_node(&self) -> Self::ConcreteNode {
- unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
- }
-
- fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
- self.as_node().owner_doc().0 as *const structs::Document == device.document() as *const _
- }
-
- fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
- if !self.may_have_style_attribute() {
- return None;
- }
-
- unsafe {
- let declarations = Gecko_GetStyleAttrDeclarationBlock(self.0).as_ref()?;
- Some(ArcBorrow::from_ref(declarations))
- }
- }
-
- fn unset_dirty_style_attribute(&self) {
- if !self.may_have_style_attribute() {
- return;
- }
-
- unsafe { Gecko_UnsetDirtyStyleAttr(self.0) };
- }
-
- fn smil_override(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
- unsafe {
- let slots = self.extended_slots()?;
-
- let declaration: &structs::DeclarationBlock =
- slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?;
-
- let raw: &structs::StyleLockedDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?;
- Some(ArcBorrow::from_ref(raw))
- }
- }
-
- fn animation_rule(
- &self,
- _: &SharedStyleContext,
- ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
- get_animation_rule(self, CascadeLevel::Animations)
- }
-
- fn transition_rule(
- &self,
- _: &SharedStyleContext,
- ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
- get_animation_rule(self, CascadeLevel::Transitions)
- }
-
- #[inline]
- fn state(&self) -> ElementState {
- ElementState::from_bits_truncate(self.state_internal())
- }
-
- #[inline]
- fn has_part_attr(&self) -> bool {
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasPart)
- }
-
- #[inline]
- fn exports_any_part(&self) -> bool {
- snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("exportparts")).is_some()
- }
-
- // FIXME(emilio): we should probably just return a reference to the Atom.
- #[inline]
- fn id(&self) -> Option<&WeakAtom> {
- if !self.has_id() {
- return None;
- }
-
- snapshot_helpers::get_id(self.non_mapped_attrs())
- }
-
- fn each_attr_name<F>(&self, mut callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- for attr in self.iter_attrs() {
- unsafe {
- AtomIdent::with(attr.mName.name(), |a| callback(a))
- }
- }
- }
-
- fn each_class<F>(&self, callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- let attr = match self.get_class_attr() {
- Some(c) => c,
- None => return,
- };
-
- snapshot_helpers::each_class_or_part(attr, callback)
- }
-
- #[inline]
- fn each_exported_part<F>(&self, name: &AtomIdent, callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- snapshot_helpers::each_exported_part(self.non_mapped_attrs(), name, callback)
- }
-
- fn each_part<F>(&self, callback: F)
- where
- F: FnMut(&AtomIdent),
- {
- let attr = match self.get_part_attr() {
- Some(c) => c,
- None => return,
- };
-
- snapshot_helpers::each_class_or_part(attr, callback)
- }
-
- #[inline]
- fn has_snapshot(&self) -> bool {
- self.flags() & ELEMENT_HAS_SNAPSHOT != 0
- }
-
- #[inline]
- fn handled_snapshot(&self) -> bool {
- self.flags() & ELEMENT_HANDLED_SNAPSHOT != 0
- }
-
- unsafe fn set_handled_snapshot(&self) {
- debug_assert!(self.has_data());
- self.set_flags(ELEMENT_HANDLED_SNAPSHOT)
- }
-
- #[inline]
- fn has_dirty_descendants(&self) -> bool {
- self.flags() & ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO != 0
- }
-
- unsafe fn set_dirty_descendants(&self) {
- debug_assert!(self.has_data());
- debug!("Setting dirty descendants: {:?}", self);
- self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
- }
-
- unsafe fn unset_dirty_descendants(&self) {
- self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
- }
-
- #[inline]
- fn has_animation_only_dirty_descendants(&self) -> bool {
- self.flags() & ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO != 0
- }
-
- unsafe fn set_animation_only_dirty_descendants(&self) {
- self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
- }
-
- unsafe fn unset_animation_only_dirty_descendants(&self) {
- self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
- }
-
- unsafe fn clear_descendant_bits(&self) {
- self.unset_flags(
- ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO |
- ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO |
- NODE_DESCENDANTS_NEED_FRAMES,
- )
- }
-
- fn is_visited_link(&self) -> bool {
- self.state().intersects(ElementState::VISITED)
- }
-
- /// We want to match rules from the same tree in all cases, except for native anonymous content
- /// that _isn't_ part directly of a UA widget (e.g., such generated by form controls, or
- /// pseudo-elements).
- #[inline]
- fn matches_user_and_content_rules(&self) -> bool {
- use crate::gecko_bindings::structs::{
- NODE_HAS_BEEN_IN_UA_WIDGET, NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE,
- };
- let flags = self.flags();
- (flags & NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) == 0 ||
- (flags & NODE_HAS_BEEN_IN_UA_WIDGET) != 0
- }
-
- #[inline]
- fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
- if self.matches_user_and_content_rules() {
- return None;
- }
-
- if !self.has_properties() {
- return None;
- }
-
- PseudoElement::from_pseudo_type(unsafe { bindings::Gecko_GetImplementedPseudo(self.0) })
- }
-
- #[inline]
- fn store_children_to_process(&self, _: isize) {
- // This is only used for bottom-up traversal, and is thus a no-op for Gecko.
- }
-
- fn did_process_child(&self) -> isize {
- panic!("Atomic child count not implemented in Gecko");
- }
-
- unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
- if !self.has_data() {
- debug!("Creating ElementData for {:?}", self);
- let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::default())));
- self.0.mServoData.set(ptr);
- }
- self.mutate_data().unwrap()
- }
-
- unsafe fn clear_data(&self) {
- let ptr = self.0.mServoData.get();
- self.unset_flags(
- ELEMENT_HAS_SNAPSHOT |
- ELEMENT_HANDLED_SNAPSHOT |
- structs::Element_kAllServoDescendantBits |
- NODE_NEEDS_FRAME,
- );
- if !ptr.is_null() {
- debug!("Dropping ElementData for {:?}", self);
- let data = Box::from_raw(self.0.mServoData.get());
- self.0.mServoData.set(ptr::null_mut());
-
- // Perform a mutable borrow of the data in debug builds. This
- // serves as an assertion that there are no outstanding borrows
- // when we destroy the data.
- debug_assert!({
- let _ = data.borrow_mut();
- true
- });
- }
- }
-
- #[inline]
- fn skip_item_display_fixup(&self) -> bool {
- debug_assert!(
- !self.is_pseudo_element(),
- "Just don't call me if I'm a pseudo, you should know the answer already"
- );
- self.is_root_of_native_anonymous_subtree()
- }
-
- #[inline]
- fn may_have_animations(&self) -> bool {
- if let Some(pseudo) = self.implemented_pseudo_element() {
- if pseudo.animations_stored_in_parent() {
- // FIXME(emilio): When would the parent of a ::before / ::after
- // pseudo-element be null?
- return self.parent_element().map_or(false, |p| {
- p.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
- });
- }
- }
- self.as_node()
- .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
- }
-
- /// Process various tasks that are a result of animation-only restyle.
- fn process_post_animation(&self, tasks: PostAnimationTasks) {
- debug_assert!(!tasks.is_empty(), "Should be involved a task");
-
- // If display style was changed from none to other, we need to resolve
- // the descendants in the display:none subtree. Instead of resolving
- // those styles in animation-only restyle, we defer it to a subsequent
- // normal restyle.
- if tasks.intersects(PostAnimationTasks::DISPLAY_CHANGED_FROM_NONE_FOR_SMIL) {
- debug_assert!(
- self.implemented_pseudo_element()
- .map_or(true, |p| !p.is_before_or_after()),
- "display property animation shouldn't run on pseudo elements \
- since it's only for SMIL"
- );
- unsafe {
- self.note_explicit_hints(
- RestyleHint::restyle_subtree(),
- nsChangeHint::nsChangeHint_Empty,
- );
- }
- }
- }
-
- /// Update various animation-related state on a given (pseudo-)element as
- /// results of normal restyle.
- fn update_animations(
- &self,
- before_change_style: Option<Arc<ComputedValues>>,
- tasks: UpdateAnimationsTasks,
- ) {
- // We have to update animations even if the element has no computed
- // style since it means the element is in a display:none subtree, we
- // should destroy all CSS animations in display:none subtree.
- let computed_data = self.borrow_data();
- let computed_values = computed_data.as_ref().map(|d| d.styles.primary());
- let before_change_values = before_change_style
- .as_ref()
- .map_or(ptr::null(), |x| x.as_gecko_computed_style());
- let computed_values_opt = computed_values
- .as_ref()
- .map_or(ptr::null(), |x| x.as_gecko_computed_style());
- unsafe {
- Gecko_UpdateAnimations(
- self.0,
- before_change_values,
- computed_values_opt,
- tasks.bits(),
- );
- }
- }
-
- #[inline]
- fn has_animations(&self, _: &SharedStyleContext) -> bool {
- self.has_any_animation()
- }
-
- fn has_css_animations(&self, _: &SharedStyleContext, _: Option<PseudoElement>) -> bool {
- self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) }
- }
-
- fn has_css_transitions(&self, _: &SharedStyleContext, _: Option<PseudoElement>) -> bool {
- self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }
- }
-
- // Detect if there are any changes that require us to update transitions.
- //
- // This is used as a more thoroughgoing check than the cheaper
- // might_need_transitions_update check.
- //
- // The following logic shadows the logic used on the Gecko side
- // (nsTransitionManager::DoUpdateTransitions) where we actually perform the
- // update.
- //
- // https://drafts.csswg.org/css-transitions/#starting
- fn needs_transitions_update(
- &self,
- before_change_style: &ComputedValues,
- after_change_style: &ComputedValues,
- ) -> bool {
- use crate::properties::LonghandIdSet;
-
- let after_change_ui_style = after_change_style.get_ui();
- let existing_transitions = self.css_transitions_info();
-
- if after_change_style.get_box().clone_display().is_none() {
- // We need to cancel existing transitions.
- return !existing_transitions.is_empty();
- }
-
- let mut transitions_to_keep = LonghandIdSet::new();
- for transition_property in after_change_style.transition_properties() {
- let physical_longhand = transition_property
- .longhand_id
- .to_physical(after_change_style.writing_mode);
- transitions_to_keep.insert(physical_longhand);
- if self.needs_transitions_update_per_property(
- physical_longhand,
- after_change_ui_style
- .transition_combined_duration_at(transition_property.index)
- .seconds(),
- before_change_style,
- after_change_style,
- &existing_transitions,
- ) {
- return true;
- }
- }
-
- // Check if we have to cancel the running transition because this is not
- // a matching transition-property value.
- existing_transitions
- .keys()
- .any(|property| !transitions_to_keep.contains(*property))
- }
-
- /// Whether there is an ElementData container.
- #[inline]
- fn has_data(&self) -> bool {
- self.get_data().is_some()
- }
-
- /// Immutably borrows the ElementData.
- fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
- self.get_data().map(|x| x.borrow())
- }
-
- /// Mutably borrows the ElementData.
- fn mutate_data(&self) -> Option<AtomicRefMut<ElementData>> {
- self.get_data().map(|x| x.borrow_mut())
- }
-
- #[inline]
- fn lang_attr(&self) -> Option<AttrValue> {
- let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
- if ptr.is_null() {
- None
- } else {
- Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))
- }
- }
-
- fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool {
- // Gecko supports :lang() from CSS Selectors 4, which accepts a list
- // of language tags, and does BCP47-style range matching.
- let override_lang_ptr = match override_lang {
- Some(Some(ref atom)) => atom.as_ptr(),
- _ => ptr::null_mut(),
- };
- value.0.iter().any(|lang| unsafe {
- Gecko_MatchLang(
- self.0,
- override_lang_ptr,
- override_lang.is_some(),
- lang.as_slice().as_ptr(),
- )
- })
- }
-
- fn is_html_document_body_element(&self) -> bool {
- if self.local_name() != &**local_name!("body") {
- return false;
- }
-
- if !self.is_html_element() {
- return false;
- }
-
- unsafe { bindings::Gecko_IsDocumentBody(self.0) }
- }
-
- fn synthesize_presentational_hints_for_legacy_attributes<V>(
- &self,
- visited_handling: VisitedHandlingMode,
- hints: &mut V,
- ) where
- V: Push<ApplicableDeclarationBlock>,
- {
- use crate::properties::longhands::_x_lang::SpecifiedValue as SpecifiedLang;
- use crate::properties::longhands::color::SpecifiedValue as SpecifiedColor;
- use crate::stylesheets::layer_rule::LayerOrder;
- use crate::values::specified::{color::Color, font::XTextScale};
- lazy_static! {
- static ref TABLE_COLOR_RULE: ApplicableDeclarationBlock = {
- let global_style_data = &*GLOBAL_STYLE_DATA;
- let pdb = PropertyDeclarationBlock::with_one(
- PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())),
- Importance::Normal,
- );
- let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
- ApplicableDeclarationBlock::from_declarations(
- arc,
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- )
- };
- static ref MATHML_LANG_RULE: ApplicableDeclarationBlock = {
- let global_style_data = &*GLOBAL_STYLE_DATA;
- let pdb = PropertyDeclarationBlock::with_one(
- PropertyDeclaration::XLang(SpecifiedLang(atom!("x-math"))),
- Importance::Normal,
- );
- let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
- ApplicableDeclarationBlock::from_declarations(
- arc,
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- )
- };
- static ref SVG_TEXT_DISABLE_SCALE_RULE: ApplicableDeclarationBlock = {
- let global_style_data = &*GLOBAL_STYLE_DATA;
- let pdb = PropertyDeclarationBlock::with_one(
- PropertyDeclaration::XTextScale(XTextScale::None),
- Importance::Normal,
- );
- let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
- ApplicableDeclarationBlock::from_declarations(
- arc,
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- )
- };
- };
-
- let ns = self.namespace_id();
- // <th> elements get a default MozCenterOrInherit which may get overridden
- if ns == structs::kNameSpaceID_XHTML as i32 {
- if self.local_name().as_ptr() == atom!("table").as_ptr() &&
- self.as_node().owner_doc().quirks_mode() == QuirksMode::Quirks
- {
- hints.push(TABLE_COLOR_RULE.clone());
- }
- }
- if ns == structs::kNameSpaceID_SVG as i32 {
- if self.local_name().as_ptr() == atom!("text").as_ptr() {
- hints.push(SVG_TEXT_DISABLE_SCALE_RULE.clone());
- }
- }
- let declarations =
- unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0).as_ref() };
- if let Some(decl) = declarations {
- hints.push(ApplicableDeclarationBlock::from_declarations(
- unsafe { Arc::from_raw_addrefed(decl) },
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ));
- }
- let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0).as_ref() };
- if let Some(decl) = declarations {
- hints.push(ApplicableDeclarationBlock::from_declarations(
- unsafe { Arc::from_raw_addrefed(decl) },
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ));
- }
-
- // Support for link, vlink, and alink presentation hints on <body>
- if self.is_link() {
- // Unvisited vs. visited styles are computed up-front based on the
- // visited mode (not the element's actual state).
- let declarations = match visited_handling {
- VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
- unreachable!(
- "We should never try to selector match with \
- AllLinksVisitedAndUnvisited"
- );
- },
- VisitedHandlingMode::AllLinksUnvisited => unsafe {
- Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0).as_ref()
- },
- VisitedHandlingMode::RelevantLinkVisited => unsafe {
- Gecko_GetVisitedLinkAttrDeclarationBlock(self.0).as_ref()
- },
- };
- if let Some(decl) = declarations {
- hints.push(ApplicableDeclarationBlock::from_declarations(
- unsafe { Arc::from_raw_addrefed(decl) },
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ));
- }
-
- let active = self
- .state()
- .intersects(NonTSPseudoClass::Active.state_flag());
- if active {
- let declarations =
- unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0).as_ref() };
- if let Some(decl) = declarations {
- hints.push(ApplicableDeclarationBlock::from_declarations(
- unsafe { Arc::from_raw_addrefed(decl) },
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ));
- }
- }
- }
-
- // xml:lang has precedence over lang, which can be
- // set by Gecko_GetHTMLPresentationAttrDeclarationBlock
- //
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language
- let ptr = unsafe { bindings::Gecko_GetXMLLangValue(self.0) };
- if !ptr.is_null() {
- let global_style_data = &*GLOBAL_STYLE_DATA;
-
- let pdb = PropertyDeclarationBlock::with_one(
- PropertyDeclaration::XLang(SpecifiedLang(unsafe { Atom::from_addrefed(ptr) })),
- Importance::Normal,
- );
- let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
- hints.push(ApplicableDeclarationBlock::from_declarations(
- arc,
- ServoCascadeLevel::PresHints,
- LayerOrder::root(),
- ))
- }
- // MathML's default lang has precedence over both `lang` and `xml:lang`
- if ns == structs::kNameSpaceID_MathML as i32 {
- if self.local_name().as_ptr() == atom!("math").as_ptr() {
- hints.push(MATHML_LANG_RULE.clone());
- }
- }
- }
-}
-
-impl<'le> PartialEq for GeckoElement<'le> {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.0 as *const _ == other.0 as *const _
- }
-}
-
-impl<'le> Eq for GeckoElement<'le> {}
-
-impl<'le> Hash for GeckoElement<'le> {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- (self.0 as *const RawGeckoElement).hash(state);
- }
-}
-
-impl<'le> ::selectors::Element for GeckoElement<'le> {
- type Impl = SelectorImpl;
-
- #[inline]
- fn opaque(&self) -> OpaqueElement {
- OpaqueElement::new(self.0)
- }
-
- #[inline]
- fn parent_element(&self) -> Option<Self> {
- let parent_node = self.as_node().parent_node();
- parent_node.and_then(|n| n.as_element())
- }
-
- #[inline]
- fn parent_node_is_shadow_root(&self) -> bool {
- self.as_node()
- .parent_node()
- .map_or(false, |p| p.is_shadow_root())
- }
-
- #[inline]
- fn containing_shadow_host(&self) -> Option<Self> {
- let shadow = self.containing_shadow()?;
- Some(shadow.host())
- }
-
- #[inline]
- fn is_pseudo_element(&self) -> bool {
- self.implemented_pseudo_element().is_some()
- }
-
- #[inline]
- fn pseudo_element_originating_element(&self) -> Option<Self> {
- debug_assert!(self.is_pseudo_element());
- debug_assert!(!self.matches_user_and_content_rules());
- let mut current = *self;
- loop {
- if current.is_root_of_native_anonymous_subtree() {
- return current.traversal_parent();
- }
-
- current = current.traversal_parent()?;
- }
- }
-
- #[inline]
- fn assigned_slot(&self) -> Option<Self> {
- let slot = self.extended_slots()?._base.mAssignedSlot.mRawPtr;
-
- unsafe { Some(GeckoElement(&slot.as_ref()?._base._base._base._base)) }
- }
-
- #[inline]
- fn prev_sibling_element(&self) -> Option<Self> {
- let mut sibling = self.as_node().prev_sibling();
- while let Some(sibling_node) = sibling {
- if let Some(el) = sibling_node.as_element() {
- return Some(el);
- }
- sibling = sibling_node.prev_sibling();
- }
- None
- }
-
- #[inline]
- fn next_sibling_element(&self) -> Option<Self> {
- let mut sibling = self.as_node().next_sibling();
- while let Some(sibling_node) = sibling {
- if let Some(el) = sibling_node.as_element() {
- return Some(el);
- }
- sibling = sibling_node.next_sibling();
- }
- None
- }
-
- #[inline]
- fn first_element_child(&self) -> Option<Self> {
- let mut child = self.as_node().first_child();
- while let Some(child_node) = child {
- if let Some(el) = child_node.as_element() {
- return Some(el);
- }
- child = child_node.next_sibling();
- }
- None
- }
-
- fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
- // Handle flags that apply to the element.
- let self_flags = flags.for_self();
- if !self_flags.is_empty() {
- self.set_flags(selector_flags_to_node_flags(flags))
- }
-
- // Handle flags that apply to the parent.
- let parent_flags = flags.for_parent();
- if !parent_flags.is_empty() {
- if let Some(p) = self.as_node().parent_node() {
- if p.is_element() || p.is_shadow_root() {
- p.set_flags(selector_flags_to_node_flags(parent_flags));
- }
- }
- }
- }
-
- fn has_attr_in_no_namespace(&self, local_name: &LocalName) -> bool {
- for attr in self.iter_attrs() {
- if attr.mName.mBits == local_name.as_ptr() as usize {
- return true;
- }
- }
- false
- }
-
- fn attr_matches(
- &self,
- ns: &NamespaceConstraint<&Namespace>,
- local_name: &LocalName,
- operation: &AttrSelectorOperation<&AttrValue>,
- ) -> bool {
- if self.attrs().is_none() {
- return false;
- }
- snapshot_helpers::attr_matches(self.iter_attrs(), ns, local_name, operation)
- }
-
- #[inline]
- fn is_root(&self) -> bool {
- if self
- .as_node()
- .get_bool_flag(nsINode_BooleanFlag::ParentIsContent)
- {
- return false;
- }
-
- if !self.as_node().is_in_document() {
- return false;
- }
-
- debug_assert!(self
- .as_node()
- .parent_node()
- .map_or(false, |p| p.is_document()));
- // XXX this should always return true at this point, shouldn't it?
- unsafe { bindings::Gecko_IsRootElement(self.0) }
- }
-
- fn is_empty(&self) -> bool {
- !self
- .as_node()
- .dom_children()
- .any(|child| unsafe { Gecko_IsSignificantChild(child.0, true) })
- }
-
- #[inline]
- fn has_local_name(&self, name: &WeakAtom) -> bool {
- self.local_name() == name
- }
-
- #[inline]
- fn has_namespace(&self, ns: &WeakNamespace) -> bool {
- self.namespace() == ns
- }
-
- #[inline]
- fn is_same_type(&self, other: &Self) -> bool {
- self.local_name() == other.local_name() && self.namespace() == other.namespace()
- }
-
- fn match_non_ts_pseudo_class(
- &self,
- pseudo_class: &NonTSPseudoClass,
- context: &mut MatchingContext<Self::Impl>,
- ) -> bool {
- use selectors::matching::*;
- match *pseudo_class {
- NonTSPseudoClass::Autofill |
- NonTSPseudoClass::Defined |
- NonTSPseudoClass::Focus |
- NonTSPseudoClass::Enabled |
- NonTSPseudoClass::Disabled |
- NonTSPseudoClass::Checked |
- NonTSPseudoClass::Fullscreen |
- NonTSPseudoClass::Indeterminate |
- NonTSPseudoClass::MozInert |
- NonTSPseudoClass::PopoverOpen |
- NonTSPseudoClass::PlaceholderShown |
- NonTSPseudoClass::Target |
- NonTSPseudoClass::Valid |
- NonTSPseudoClass::Invalid |
- NonTSPseudoClass::MozBroken |
- NonTSPseudoClass::MozLoading |
- NonTSPseudoClass::Required |
- NonTSPseudoClass::Optional |
- NonTSPseudoClass::ReadOnly |
- NonTSPseudoClass::ReadWrite |
- NonTSPseudoClass::FocusWithin |
- NonTSPseudoClass::FocusVisible |
- NonTSPseudoClass::MozDragOver |
- NonTSPseudoClass::MozDevtoolsHighlighted |
- NonTSPseudoClass::MozStyleeditorTransitioning |
- NonTSPseudoClass::MozMathIncrementScriptLevel |
- NonTSPseudoClass::InRange |
- NonTSPseudoClass::OutOfRange |
- NonTSPseudoClass::Default |
- NonTSPseudoClass::UserValid |
- NonTSPseudoClass::UserInvalid |
- NonTSPseudoClass::MozMeterOptimum |
- NonTSPseudoClass::MozMeterSubOptimum |
- NonTSPseudoClass::MozMeterSubSubOptimum |
- NonTSPseudoClass::MozHasDirAttr |
- NonTSPseudoClass::MozDirAttrLTR |
- NonTSPseudoClass::MozDirAttrRTL |
- NonTSPseudoClass::MozDirAttrLikeAuto |
- NonTSPseudoClass::Modal |
- NonTSPseudoClass::MozTopmostModal |
- NonTSPseudoClass::Active |
- NonTSPseudoClass::Hover |
- NonTSPseudoClass::MozAutofillPreview |
- NonTSPseudoClass::MozRevealed |
- NonTSPseudoClass::MozValueEmpty |
- NonTSPseudoClass::Dir(..) => self.state().intersects(pseudo_class.state_flag()),
- NonTSPseudoClass::AnyLink => self.is_link(),
- NonTSPseudoClass::Link => {
- self.is_link() && context.visited_handling().matches_unvisited()
- },
- NonTSPseudoClass::Visited => {
- self.is_link() && context.visited_handling().matches_visited()
- },
- NonTSPseudoClass::MozFirstNode => {
- if context.needs_selector_flags() {
- self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
- }
- let mut elem = self.as_node();
- while let Some(prev) = elem.prev_sibling() {
- if prev.contains_non_whitespace_content() {
- return false;
- }
- elem = prev;
- }
- true
- },
- NonTSPseudoClass::MozLastNode => {
- if context.needs_selector_flags() {
- self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
- }
- let mut elem = self.as_node();
- while let Some(next) = elem.next_sibling() {
- if next.contains_non_whitespace_content() {
- return false;
- }
- elem = next;
- }
- true
- },
- NonTSPseudoClass::MozOnlyWhitespace => {
- if context.needs_selector_flags() {
- self.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR);
- }
- if self
- .as_node()
- .dom_children()
- .any(|c| c.contains_non_whitespace_content())
- {
- return false;
- }
- true
- },
- NonTSPseudoClass::MozNativeAnonymous => !self.matches_user_and_content_rules(),
- NonTSPseudoClass::MozUseShadowTreeRoot => self.is_root_of_use_element_shadow_tree(),
- NonTSPseudoClass::MozTableBorderNonzero => unsafe {
- bindings::Gecko_IsTableBorderNonzero(self.0)
- },
- NonTSPseudoClass::MozBrowserFrame => unsafe { bindings::Gecko_IsBrowserFrame(self.0) },
- NonTSPseudoClass::MozSelectListBox => unsafe {
- bindings::Gecko_IsSelectListBox(self.0)
- },
- NonTSPseudoClass::MozIsHTML => self.is_html_element_in_html_document(),
-
- NonTSPseudoClass::MozLWTheme |
- NonTSPseudoClass::MozLocaleDir(..) |
- NonTSPseudoClass::MozWindowInactive => {
- let state_bit = pseudo_class.document_state_flag();
- if state_bit.is_empty() {
- debug_assert!(
- matches!(pseudo_class, NonTSPseudoClass::MozLocaleDir(..)),
- "Only moz-locale-dir should ever return an empty state"
- );
- return false;
- }
- if context
- .extra_data
- .invalidation_data
- .document_state
- .intersects(state_bit)
- {
- return !context.in_negation();
- }
- self.document_state().contains(state_bit)
- },
- NonTSPseudoClass::MozPlaceholder => false,
- NonTSPseudoClass::Lang(ref lang_arg) => self.match_element_lang(None, lang_arg),
- }
- }
-
- fn match_pseudo_element(
- &self,
- pseudo_element: &PseudoElement,
- _context: &mut MatchingContext<Self::Impl>,
- ) -> bool {
- // TODO(emilio): I believe we could assert we are a pseudo-element and
- // match the proper pseudo-element, given how we rulehash the stuff
- // based on the pseudo.
- match self.implemented_pseudo_element() {
- Some(ref pseudo) => *pseudo == *pseudo_element,
- None => false,
- }
- }
-
- #[inline]
- fn is_link(&self) -> bool {
- self.state().intersects(ElementState::VISITED_OR_UNVISITED)
- }
-
- #[inline]
- fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
- if !self.has_id() {
- return false;
- }
-
- let element_id = match snapshot_helpers::get_id(self.non_mapped_attrs()) {
- Some(id) => id,
- None => return false,
- };
-
- case_sensitivity.eq_atom(element_id, id)
- }
-
- #[inline]
- fn is_part(&self, name: &AtomIdent) -> bool {
- let attr = match self.get_part_attr() {
- Some(c) => c,
- None => return false,
- };
-
- snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
- }
-
- #[inline]
- fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
- snapshot_helpers::imported_part(self.non_mapped_attrs(), name)
- }
-
- #[inline(always)]
- fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
- let attr = match self.get_class_attr() {
- Some(c) => c,
- None => return false,
- };
-
- snapshot_helpers::has_class_or_part(name, case_sensitivity, attr)
- }
-
- #[inline]
- fn is_html_element_in_html_document(&self) -> bool {
- self.is_html_element() && self.as_node().owner_doc().is_html_document()
- }
-
- #[inline]
- fn is_html_slot_element(&self) -> bool {
- self.is_html_element() && self.local_name().as_ptr() == local_name!("slot").as_ptr()
- }
-
- #[inline]
- fn ignores_nth_child_selectors(&self) -> bool {
- self.is_root_of_native_anonymous_subtree()
- }
-}