tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

wrapper.rs (78587B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      4 
      5 #![allow(unsafe_code)]
      6 
      7 //! Wrapper definitions on top of Gecko types in order to be used in the style
      8 //! system.
      9 //!
     10 //! This really follows the Servo pattern in
     11 //! `components/script/layout_wrapper.rs`.
     12 //!
     13 //! This theoretically should live in its own crate, but now it lives in the
     14 //! style system it's kind of pointless in the Stylo case, and only Servo forces
     15 //! the separation between the style system implementation and everything else.
     16 
     17 use crate::applicable_declarations::ApplicableDeclarationBlock;
     18 use crate::bloom::each_relevant_element_hash;
     19 use crate::context::{QuirksMode, SharedStyleContext, UpdateAnimationsTasks};
     20 use crate::data::ElementData;
     21 use crate::dom::{
     22    AttributeProvider, LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode,
     23    TShadowRoot,
     24 };
     25 use crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};
     26 use crate::gecko::snapshot_helpers;
     27 use crate::gecko_bindings::bindings;
     28 use crate::gecko_bindings::bindings::Gecko_ElementHasAnimations;
     29 use crate::gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
     30 use crate::gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
     31 use crate::gecko_bindings::bindings::Gecko_ElementState;
     32 use crate::gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
     33 use crate::gecko_bindings::bindings::Gecko_GetAnimationEffectCount;
     34 use crate::gecko_bindings::bindings::Gecko_GetAnimationRule;
     35 use crate::gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;
     36 use crate::gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
     37 use crate::gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock;
     38 use crate::gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock;
     39 use crate::gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock;
     40 use crate::gecko_bindings::bindings::Gecko_IsSignificantChild;
     41 use crate::gecko_bindings::bindings::Gecko_MatchLang;
     42 use crate::gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr;
     43 use crate::gecko_bindings::bindings::Gecko_UpdateAnimations;
     44 use crate::gecko_bindings::structs;
     45 use crate::gecko_bindings::structs::nsChangeHint;
     46 use crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
     47 use crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
     48 use crate::gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
     49 use crate::gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
     50 use crate::gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
     51 use crate::gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES;
     52 use crate::gecko_bindings::structs::NODE_NEEDS_FRAME;
     53 use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag};
     54 use crate::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement};
     55 use crate::global_style_data::GLOBAL_STYLE_DATA;
     56 use crate::invalidation::element::restyle_hints::RestyleHint;
     57 use crate::media_queries::Device;
     58 use crate::properties::{
     59    animated_properties::{AnimationValue, AnimationValueMap},
     60    ComputedValues, Importance, OwnedPropertyDeclarationId, PropertyDeclaration,
     61    PropertyDeclarationBlock, PropertyDeclarationId, PropertyDeclarationIdSet,
     62 };
     63 use crate::rule_tree::CascadeLevel as ServoCascadeLevel;
     64 use crate::selector_parser::{AttrValue, Lang};
     65 use crate::shared_lock::{Locked, SharedRwLock};
     66 use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
     67 use crate::stylesheets::scope_rule::ImplicitScopeRoot;
     68 use crate::stylist::CascadeData;
     69 use crate::values::computed::Display;
     70 use crate::values::{AtomIdent, AtomString};
     71 use crate::CaseSensitivityExt;
     72 use crate::LocalName;
     73 use app_units::Au;
     74 use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
     75 use dom::{DocumentState, ElementState};
     76 use euclid::default::Size2D;
     77 use nsstring::nsString;
     78 use rustc_hash::FxHashMap;
     79 use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
     80 use selectors::bloom::{BloomFilter, BLOOM_HASH_MASK};
     81 use selectors::matching::VisitedHandlingMode;
     82 use selectors::matching::{ElementSelectorFlags, MatchingContext};
     83 use selectors::parser::PseudoElement as ParserPseudoElement;
     84 use selectors::sink::Push;
     85 use selectors::{Element, OpaqueElement};
     86 use servo_arc::{Arc, ArcBorrow};
     87 use std::cell::Cell;
     88 use std::fmt;
     89 use std::hash::{Hash, Hasher};
     90 use std::mem;
     91 use std::ptr;
     92 use std::sync::atomic::{AtomicU32, Ordering};
     93 
     94 #[inline]
     95 fn elements_with_id<'a, 'le>(
     96    array: structs::RustSpan<*const RawGeckoElement>,
     97 ) -> &'a [GeckoElement<'le>] {
     98    unsafe {
     99        let elements: &[*const RawGeckoElement] =
    100            std::slice::from_raw_parts(array.begin, array.length);
    101 
    102        // NOTE(emilio): We rely on the in-memory representation of
    103        // GeckoElement<'ld> and *const RawGeckoElement being the same.
    104        #[allow(dead_code)]
    105        unsafe fn static_assert() {
    106            mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _);
    107        }
    108 
    109        mem::transmute(elements)
    110    }
    111 }
    112 
    113 /// A simple wrapper over `Document`.
    114 #[derive(Clone, Copy)]
    115 pub struct GeckoDocument<'ld>(pub &'ld structs::Document);
    116 
    117 impl<'ld> TDocument for GeckoDocument<'ld> {
    118    type ConcreteNode = GeckoNode<'ld>;
    119 
    120    #[inline]
    121    fn as_node(&self) -> Self::ConcreteNode {
    122        GeckoNode(&self.0._base)
    123    }
    124 
    125    #[inline]
    126    fn is_html_document(&self) -> bool {
    127        self.0.mType == structs::Document_Type::eHTML
    128    }
    129 
    130    #[inline]
    131    fn quirks_mode(&self) -> QuirksMode {
    132        self.0.mCompatMode.into()
    133    }
    134 
    135    #[inline]
    136    fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'ld>], ()>
    137    where
    138        Self: 'a,
    139    {
    140        Ok(elements_with_id(unsafe {
    141            bindings::Gecko_Document_GetElementsWithId(self.0, id.as_ptr())
    142        }))
    143    }
    144 
    145    fn shared_lock(&self) -> &SharedRwLock {
    146        &GLOBAL_STYLE_DATA.shared_lock
    147    }
    148 }
    149 
    150 /// A simple wrapper over `ShadowRoot`.
    151 #[derive(Clone, Copy)]
    152 pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
    153 
    154 impl<'ln> fmt::Debug for GeckoShadowRoot<'ln> {
    155    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    156        // TODO(emilio): Maybe print the host or something?
    157        write!(f, "<shadow-root> ({:#x})", self.as_node().opaque().0)
    158    }
    159 }
    160 
    161 impl<'lr> PartialEq for GeckoShadowRoot<'lr> {
    162    #[inline]
    163    fn eq(&self, other: &Self) -> bool {
    164        self.0 as *const _ == other.0 as *const _
    165    }
    166 }
    167 
    168 impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
    169    type ConcreteNode = GeckoNode<'lr>;
    170 
    171    #[inline]
    172    fn as_node(&self) -> Self::ConcreteNode {
    173        GeckoNode(&self.0._base._base._base._base)
    174    }
    175 
    176    #[inline]
    177    fn host(&self) -> GeckoElement<'lr> {
    178        GeckoElement(unsafe { &*self.0._base.mHost.mRawPtr })
    179    }
    180 
    181    #[inline]
    182    fn style_data<'a>(&self) -> Option<&'a CascadeData>
    183    where
    184        Self: 'a,
    185    {
    186        let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? };
    187        Some(&author_styles.data)
    188    }
    189 
    190    #[inline]
    191    fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'lr>], ()>
    192    where
    193        Self: 'a,
    194    {
    195        Ok(elements_with_id(unsafe {
    196            bindings::Gecko_ShadowRoot_GetElementsWithId(self.0, id.as_ptr())
    197        }))
    198    }
    199 
    200    #[inline]
    201    fn parts<'a>(&self) -> &[<Self::ConcreteNode as TNode>::ConcreteElement]
    202    where
    203        Self: 'a,
    204    {
    205        let slice: &[*const RawGeckoElement] = &*self.0.mParts;
    206 
    207        #[allow(dead_code)]
    208        unsafe fn static_assert() {
    209            mem::transmute::<*const RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *const _);
    210        }
    211 
    212        unsafe { mem::transmute(slice) }
    213    }
    214 
    215    #[inline]
    216    fn implicit_scope_for_sheet(&self, sheet_index: usize) -> Option<ImplicitScopeRoot> {
    217        use crate::stylesheets::StylesheetInDocument;
    218 
    219        let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? };
    220        let sheet = author_styles.stylesheets.get(sheet_index)?;
    221        sheet.implicit_scope_root()
    222    }
    223 }
    224 
    225 /// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
    226 ///
    227 /// Important: We don't currently refcount the DOM, because the wrapper lifetime
    228 /// magic guarantees that our LayoutFoo references won't outlive the root, and
    229 /// we don't mutate any of the references on the Gecko side during restyle.
    230 ///
    231 /// We could implement refcounting if need be (at a potentially non-trivial
    232 /// performance cost) by implementing Drop and making LayoutFoo non-Copy.
    233 #[derive(Clone, Copy)]
    234 pub struct GeckoNode<'ln>(pub &'ln RawGeckoNode);
    235 
    236 impl<'ln> PartialEq for GeckoNode<'ln> {
    237    #[inline]
    238    fn eq(&self, other: &Self) -> bool {
    239        self.0 as *const _ == other.0 as *const _
    240    }
    241 }
    242 
    243 impl<'ln> fmt::Debug for GeckoNode<'ln> {
    244    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    245        if let Some(el) = self.as_element() {
    246            return el.fmt(f);
    247        }
    248 
    249        if self.is_text_node() {
    250            return write!(f, "<text node> ({:#x})", self.opaque().0);
    251        }
    252 
    253        if self.is_document() {
    254            return write!(f, "<document> ({:#x})", self.opaque().0);
    255        }
    256 
    257        if let Some(sr) = self.as_shadow_root() {
    258            return sr.fmt(f);
    259        }
    260 
    261        write!(f, "<non-text node> ({:#x})", self.opaque().0)
    262    }
    263 }
    264 
    265 impl<'ln> GeckoNode<'ln> {
    266    #[inline]
    267    fn is_document(&self) -> bool {
    268        // This is a DOM constant that isn't going to change.
    269        const DOCUMENT_NODE: u16 = 9;
    270        self.node_info().mInner.mNodeType == DOCUMENT_NODE
    271    }
    272 
    273    #[inline]
    274    fn is_shadow_root(&self) -> bool {
    275        self.is_in_shadow_tree() && self.parent_node().is_none()
    276    }
    277 
    278    #[inline]
    279    fn from_content(content: &'ln nsIContent) -> Self {
    280        GeckoNode(&content._base)
    281    }
    282 
    283    #[inline]
    284    fn set_flags(&self, flags: u32) {
    285        self.flags_atomic().fetch_or(flags, Ordering::Relaxed);
    286    }
    287 
    288    fn flags_atomic_for(flags: &Cell<u32>) -> &AtomicU32 {
    289        const_assert!(mem::size_of::<Cell<u32>>() == mem::size_of::<AtomicU32>());
    290        const_assert!(mem::align_of::<Cell<u32>>() == mem::align_of::<AtomicU32>());
    291 
    292        // Rust doesn't provide standalone atomic functions like GCC/clang do
    293        // (via the atomic intrinsics) or via std::atomic_ref, but it guarantees
    294        // that the memory representation of u32 and AtomicU32 matches:
    295        // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicU32.html
    296        unsafe { mem::transmute::<&Cell<u32>, &AtomicU32>(flags) }
    297    }
    298 
    299    #[inline]
    300    fn flags_atomic(&self) -> &AtomicU32 {
    301        Self::flags_atomic_for(&self.0._base._base_1.mFlags)
    302    }
    303 
    304    #[inline]
    305    fn flags(&self) -> u32 {
    306        self.flags_atomic().load(Ordering::Relaxed)
    307    }
    308 
    309    #[inline]
    310    fn selector_flags_atomic(&self) -> &AtomicU32 {
    311        Self::flags_atomic_for(&self.0.mSelectorFlags)
    312    }
    313 
    314    #[inline]
    315    fn selector_flags(&self) -> u32 {
    316        self.selector_flags_atomic().load(Ordering::Relaxed)
    317    }
    318 
    319    #[inline]
    320    fn set_selector_flags(&self, flags: u32) {
    321        self.selector_flags_atomic()
    322            .fetch_or(flags, Ordering::Relaxed);
    323    }
    324 
    325    #[inline]
    326    fn node_info(&self) -> &structs::NodeInfo {
    327        debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null());
    328        unsafe { &*self.0.mNodeInfo.mRawPtr }
    329    }
    330 
    331    // These live in different locations depending on processor architecture.
    332    #[cfg(target_pointer_width = "64")]
    333    #[inline]
    334    fn bool_flags(&self) -> u32 {
    335        (self.0)._base._base_1.mBoolFlags
    336    }
    337 
    338    #[cfg(target_pointer_width = "32")]
    339    #[inline]
    340    fn bool_flags(&self) -> u32 {
    341        (self.0).mBoolFlags
    342    }
    343 
    344    #[inline]
    345    fn get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool {
    346        self.bool_flags() & (1u32 << flag as u32) != 0
    347    }
    348 
    349    /// This logic is duplicate in Gecko's nsINode::IsInShadowTree().
    350    #[inline]
    351    fn is_in_shadow_tree(&self) -> bool {
    352        use crate::gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;
    353        self.flags() & NODE_IS_IN_SHADOW_TREE != 0
    354    }
    355 
    356    /// Returns true if we know for sure that `flattened_tree_parent` and `parent_node` return the
    357    /// same thing.
    358    ///
    359    /// TODO(emilio): Measure and consider not doing this fast-path, it's only a function call and
    360    /// from profiles it seems that keeping this fast path makes the compiler not inline
    361    /// `flattened_tree_parent` as a whole, so we're not gaining much either.
    362    #[inline]
    363    fn flattened_tree_parent_is_parent(&self) -> bool {
    364        use crate::gecko_bindings::structs::*;
    365        let flags = self.flags();
    366 
    367        let parent = match self.parent_node() {
    368            Some(p) => p,
    369            None => return true,
    370        };
    371 
    372        if parent.is_shadow_root() {
    373            return false;
    374        }
    375 
    376        if let Some(parent) = parent.as_element() {
    377            if flags & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0 && parent.is_root() {
    378                return false;
    379            }
    380            if parent.shadow_root().is_some() || parent.is_html_slot_element() {
    381                return false;
    382            }
    383        }
    384 
    385        true
    386    }
    387 
    388    #[inline]
    389    fn flattened_tree_parent(&self) -> Option<Self> {
    390        if self.flattened_tree_parent_is_parent() {
    391            debug_assert_eq!(
    392                unsafe {
    393                    bindings::Gecko_GetFlattenedTreeParentNode(self.0)
    394                        .as_ref()
    395                        .map(GeckoNode)
    396                },
    397                self.parent_node(),
    398                "Fast path stopped holding!"
    399            );
    400            return self.parent_node();
    401        }
    402 
    403        // NOTE(emilio): If this call is too expensive, we could manually inline more aggressively.
    404        unsafe {
    405            bindings::Gecko_GetFlattenedTreeParentNode(self.0)
    406                .as_ref()
    407                .map(GeckoNode)
    408        }
    409    }
    410 
    411    #[inline]
    412    fn contains_non_whitespace_content(&self) -> bool {
    413        unsafe { Gecko_IsSignificantChild(self.0, false) }
    414    }
    415 
    416    /// Returns the previous sibling of this node that is an element.
    417    #[inline]
    418    pub fn prev_sibling_element(&self) -> Option<GeckoElement<'ln>> {
    419        let mut prev = self.prev_sibling();
    420        while let Some(p) = prev {
    421            if let Some(e) = p.as_element() {
    422                return Some(e);
    423            }
    424            prev = p.prev_sibling();
    425        }
    426        None
    427    }
    428 
    429    /// Returns the next sibling of this node that is an element.
    430    #[inline]
    431    pub fn next_sibling_element(&self) -> Option<GeckoElement<'ln>> {
    432        let mut next = self.next_sibling();
    433        while let Some(n) = next {
    434            if let Some(e) = n.as_element() {
    435                return Some(e);
    436            }
    437            next = n.next_sibling();
    438        }
    439        None
    440    }
    441 
    442    /// Returns last child sibling of this node that is an element.
    443    #[inline]
    444    pub fn last_child_element(&self) -> Option<GeckoElement<'ln>> {
    445        let last = match self.last_child() {
    446            Some(n) => n,
    447            None => return None,
    448        };
    449        if let Some(e) = last.as_element() {
    450            return Some(e);
    451        }
    452        None
    453    }
    454 }
    455 
    456 impl<'ln> NodeInfo for GeckoNode<'ln> {
    457    #[inline]
    458    fn is_element(&self) -> bool {
    459        self.get_bool_flag(nsINode_BooleanFlag::NodeIsElement)
    460    }
    461 
    462    fn is_text_node(&self) -> bool {
    463        // This is a DOM constant that isn't going to change.
    464        const TEXT_NODE: u16 = 3;
    465        self.node_info().mInner.mNodeType == TEXT_NODE
    466    }
    467 }
    468 
    469 impl<'ln> TNode for GeckoNode<'ln> {
    470    type ConcreteDocument = GeckoDocument<'ln>;
    471    type ConcreteShadowRoot = GeckoShadowRoot<'ln>;
    472    type ConcreteElement = GeckoElement<'ln>;
    473 
    474    #[inline]
    475    fn parent_node(&self) -> Option<Self> {
    476        unsafe { self.0.mParent.as_ref().map(GeckoNode) }
    477    }
    478 
    479    #[inline]
    480    fn first_child(&self) -> Option<Self> {
    481        unsafe {
    482            self.0
    483                .mFirstChild
    484                .raw()
    485                .as_ref()
    486                .map(GeckoNode::from_content)
    487        }
    488    }
    489 
    490    #[inline]
    491    fn last_child(&self) -> Option<Self> {
    492        unsafe { bindings::Gecko_GetLastChild(self.0).as_ref().map(GeckoNode) }
    493    }
    494 
    495    #[inline]
    496    fn prev_sibling(&self) -> Option<Self> {
    497        unsafe {
    498            let prev_or_last = GeckoNode::from_content(self.0.mPreviousOrLastSibling.as_ref()?);
    499            if prev_or_last.0.mNextSibling.raw().is_null() {
    500                return None;
    501            }
    502            Some(prev_or_last)
    503        }
    504    }
    505 
    506    #[inline]
    507    fn next_sibling(&self) -> Option<Self> {
    508        unsafe {
    509            self.0
    510                .mNextSibling
    511                .raw()
    512                .as_ref()
    513                .map(GeckoNode::from_content)
    514        }
    515    }
    516 
    517    #[inline]
    518    fn owner_doc(&self) -> Self::ConcreteDocument {
    519        debug_assert!(!self.node_info().mDocument.is_null());
    520        GeckoDocument(unsafe { &*self.node_info().mDocument })
    521    }
    522 
    523    #[inline]
    524    fn is_in_document(&self) -> bool {
    525        self.get_bool_flag(nsINode_BooleanFlag::IsInDocument)
    526    }
    527 
    528    fn traversal_parent(&self) -> Option<GeckoElement<'ln>> {
    529        self.flattened_tree_parent().and_then(|n| n.as_element())
    530    }
    531 
    532    #[inline]
    533    fn opaque(&self) -> OpaqueNode {
    534        let ptr: usize = self.0 as *const _ as usize;
    535        OpaqueNode(ptr)
    536    }
    537 
    538    fn debug_id(self) -> usize {
    539        unimplemented!()
    540    }
    541 
    542    #[inline]
    543    fn as_element(&self) -> Option<GeckoElement<'ln>> {
    544        if !self.is_element() {
    545            return None;
    546        }
    547 
    548        Some(GeckoElement(unsafe {
    549            &*(self.0 as *const _ as *const RawGeckoElement)
    550        }))
    551    }
    552 
    553    #[inline]
    554    fn as_document(&self) -> Option<Self::ConcreteDocument> {
    555        if !self.is_document() {
    556            return None;
    557        }
    558 
    559        debug_assert_eq!(self.owner_doc().as_node(), *self, "How?");
    560        Some(self.owner_doc())
    561    }
    562 
    563    #[inline]
    564    fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {
    565        if !self.is_shadow_root() {
    566            return None;
    567        }
    568 
    569        Some(GeckoShadowRoot(unsafe {
    570            &*(self.0 as *const _ as *const structs::ShadowRoot)
    571        }))
    572    }
    573 }
    574 
    575 /// A wrapper on top of two kind of iterators, depending on the parent being
    576 /// iterated.
    577 ///
    578 /// We generally iterate children by traversing the light-tree siblings of the
    579 /// first child like Servo does.
    580 ///
    581 /// However, for nodes with anonymous children, we use a custom (heavier-weight)
    582 /// Gecko-implemented iterator.
    583 ///
    584 /// FIXME(emilio): If we take into account shadow DOM, we're going to need the
    585 /// flat tree pretty much always. We can try to optimize the case where there's
    586 /// no shadow root sibling, probably.
    587 pub enum GeckoChildrenIterator<'a> {
    588    /// A simple iterator that tracks the current node being iterated and
    589    /// replaces it with the next sibling when requested.
    590    Current(Option<GeckoNode<'a>>),
    591    /// A Gecko-implemented iterator we need to drop appropriately.
    592    GeckoIterator(mem::ManuallyDrop<structs::StyleChildrenIterator>),
    593 }
    594 
    595 impl<'a> Drop for GeckoChildrenIterator<'a> {
    596    fn drop(&mut self) {
    597        if let GeckoChildrenIterator::GeckoIterator(ref mut it) = *self {
    598            unsafe {
    599                bindings::Gecko_DestroyStyleChildrenIterator(&mut **it);
    600            }
    601        }
    602    }
    603 }
    604 
    605 impl<'a> Iterator for GeckoChildrenIterator<'a> {
    606    type Item = GeckoNode<'a>;
    607    fn next(&mut self) -> Option<GeckoNode<'a>> {
    608        match *self {
    609            GeckoChildrenIterator::Current(curr) => {
    610                let next = curr.and_then(|node| node.next_sibling());
    611                *self = GeckoChildrenIterator::Current(next);
    612                curr
    613            },
    614            GeckoChildrenIterator::GeckoIterator(ref mut it) => unsafe {
    615                // We do this unsafe lengthening of the lifetime here because
    616                // structs::StyleChildrenIterator is actually StyleChildrenIterator<'a>,
    617                // however we can't express this easily with bindgen, and it would
    618                // introduce functions with two input lifetimes into bindgen,
    619                // which would be out of scope for elision.
    620                bindings::Gecko_GetNextStyleChild(&mut **it)
    621                    .as_ref()
    622                    .map(GeckoNode)
    623            },
    624        }
    625    }
    626 }
    627 
    628 /// A simple wrapper over a non-null Gecko `Element` pointer.
    629 #[derive(Clone, Copy)]
    630 pub struct GeckoElement<'le>(pub &'le RawGeckoElement);
    631 
    632 impl<'le> fmt::Debug for GeckoElement<'le> {
    633    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    634        use nsstring::nsCString;
    635 
    636        write!(f, "<{}", self.local_name())?;
    637 
    638        let mut attrs = nsCString::new();
    639        unsafe {
    640            bindings::Gecko_Element_DebugListAttributes(self.0, &mut attrs);
    641        }
    642        write!(f, "{}", attrs)?;
    643        write!(f, "> ({:#x})", self.as_node().opaque().0)
    644    }
    645 }
    646 
    647 impl<'le> GeckoElement<'le> {
    648    /// Gets the raw `ElementData` refcell for the element.
    649    #[inline(always)]
    650    pub fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
    651        unsafe { self.0.mServoData.get().as_ref() }
    652    }
    653 
    654    /// Returns whether any animation applies to this element.
    655    #[inline]
    656    pub fn has_any_animation(&self) -> bool {
    657        self.may_have_animations() && unsafe { Gecko_ElementHasAnimations(self.0) }
    658    }
    659 
    660    /// Check if mImpl contains a real pointer (not a bloom filter).
    661    #[inline(always)]
    662    fn has_attr_impl(&self) -> bool {
    663        let ptr = self.0.mAttrs.mImpl.mPtr as usize;
    664        ptr != 0 && (ptr & 1) == 0
    665    }
    666 
    667    #[inline(always)]
    668    fn attrs(&self) -> &[structs::AttrArray_InternalAttr] {
    669        unsafe {
    670            if !self.has_attr_impl() {
    671                return &[];
    672            }
    673            match self.0.mAttrs.mImpl.mPtr.as_ref() {
    674                Some(attrs) => attrs.mBuffer.as_slice(attrs.mAttrCount as usize),
    675                None => &[],
    676            }
    677        }
    678    }
    679 
    680    #[inline(always)]
    681    fn get_part_attr(&self) -> Option<&structs::nsAttrValue> {
    682        if !self.has_part_attr() {
    683            return None;
    684        }
    685        snapshot_helpers::find_attr(self.attrs(), &atom!("part"))
    686    }
    687 
    688    #[inline(always)]
    689    fn get_class_attr(&self) -> Option<&structs::nsAttrValue> {
    690        if !self.may_have_class() {
    691            return None;
    692        }
    693 
    694        if self.is_svg_element() {
    695            let svg_class = unsafe { bindings::Gecko_GetSVGAnimatedClass(self.0).as_ref() };
    696            if let Some(c) = svg_class {
    697                return Some(c);
    698            }
    699        }
    700 
    701        snapshot_helpers::find_attr(self.attrs(), &atom!("class"))
    702    }
    703 
    704    #[inline]
    705    fn may_have_anonymous_children(&self) -> bool {
    706        self.as_node()
    707            .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveAnonymousChildren)
    708    }
    709 
    710    #[inline]
    711    fn flags(&self) -> u32 {
    712        self.as_node().flags()
    713    }
    714 
    715    #[inline]
    716    fn set_flags(&self, flags: u32) {
    717        self.as_node().set_flags(flags);
    718    }
    719 
    720    #[inline]
    721    unsafe fn unset_flags(&self, flags: u32) {
    722        self.as_node()
    723            .flags_atomic()
    724            .fetch_and(!flags, Ordering::Relaxed);
    725    }
    726 
    727    /// Returns true if this element has descendants for lazy frame construction.
    728    #[inline]
    729    pub fn descendants_need_frames(&self) -> bool {
    730        self.flags() & NODE_DESCENDANTS_NEED_FRAMES != 0
    731    }
    732 
    733    /// Returns true if this element needs lazy frame construction.
    734    #[inline]
    735    pub fn needs_frame(&self) -> bool {
    736        self.flags() & NODE_NEEDS_FRAME != 0
    737    }
    738 
    739    /// Returns a reference to the DOM slots for this Element, if they exist.
    740    #[inline]
    741    fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> {
    742        let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots;
    743        unsafe { slots.as_ref() }
    744    }
    745 
    746    /// Returns a reference to the extended DOM slots for this Element.
    747    #[inline]
    748    fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {
    749        self.dom_slots().and_then(|s| unsafe {
    750            // For the bit usage, see nsContentSlots::GetExtendedSlots.
    751            let e_slots = s._base.mExtendedSlots
    752                & !structs::nsIContent_nsContentSlots_sNonOwningExtendedSlotsFlag;
    753            (e_slots as *const structs::FragmentOrElement_nsExtendedDOMSlots).as_ref()
    754        })
    755    }
    756 
    757    #[inline]
    758    fn namespace_id(&self) -> i32 {
    759        self.as_node().node_info().mInner.mNamespaceID
    760    }
    761 
    762    #[inline]
    763    fn has_id(&self) -> bool {
    764        self.as_node()
    765            .get_bool_flag(nsINode_BooleanFlag::ElementHasID)
    766    }
    767 
    768    #[inline]
    769    fn state_internal(&self) -> u64 {
    770        if !self
    771            .as_node()
    772            .get_bool_flag(nsINode_BooleanFlag::ElementHasLockedStyleStates)
    773        {
    774            return self.0.mState.bits;
    775        }
    776        unsafe { Gecko_ElementState(self.0) }
    777    }
    778 
    779    #[inline]
    780    fn document_state(&self) -> DocumentState {
    781        DocumentState::from_bits_retain(self.as_node().owner_doc().0.mState.bits)
    782    }
    783 
    784    #[inline]
    785    fn may_have_class(&self) -> bool {
    786        self.as_node()
    787            .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveClass)
    788    }
    789 
    790    #[inline]
    791    fn has_properties(&self) -> bool {
    792        use crate::gecko_bindings::structs::NODE_HAS_PROPERTIES;
    793 
    794        self.flags() & NODE_HAS_PROPERTIES != 0
    795    }
    796 
    797    #[inline]
    798    fn may_have_style_attribute(&self) -> bool {
    799        self.as_node()
    800            .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveStyle)
    801    }
    802 
    803    /// Only safe to call on the main thread, with exclusive access to the
    804    /// element and its ancestors.
    805    ///
    806    /// This function is also called after display property changed for SMIL
    807    /// animation.
    808    ///
    809    /// Also this function schedules style flush.
    810    pub unsafe fn note_explicit_hints(&self, restyle_hint: RestyleHint, change_hint: nsChangeHint) {
    811        use crate::gecko::restyle_damage::GeckoRestyleDamage;
    812 
    813        let damage = GeckoRestyleDamage::new(change_hint);
    814        debug!(
    815            "note_explicit_hints: {:?}, restyle_hint={:?}, change_hint={:?}",
    816            self, restyle_hint, change_hint
    817        );
    818        debug_assert!(bindings::Gecko_IsMainThread());
    819        debug_assert!(
    820            !(restyle_hint.has_animation_hint() && restyle_hint.has_non_animation_hint()),
    821            "Animation restyle hints should not appear with non-animation restyle hints"
    822        );
    823 
    824        // See comments on borrow_assert_main_thread and co.
    825        let data = match self.get_data() {
    826            Some(d) => d,
    827            None => {
    828                debug!("(Element not styled, discarding hints)");
    829                return;
    830            },
    831        };
    832 
    833        // Propagate the bit up the chain.
    834        if restyle_hint.has_animation_hint() {
    835            bindings::Gecko_NoteAnimationOnlyDirtyElement(self.0);
    836        } else {
    837            bindings::Gecko_NoteDirtyElement(self.0);
    838        }
    839 
    840        #[cfg(debug_assertions)]
    841        let mut data = data.borrow_mut();
    842        #[cfg(not(debug_assertions))]
    843        let data = &mut *data.as_ptr();
    844 
    845        data.hint.insert(restyle_hint);
    846        data.damage |= damage;
    847    }
    848 
    849    /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree.
    850    #[inline]
    851    fn is_root_of_native_anonymous_subtree(&self) -> bool {
    852        return self.flags() & structs::NODE_IS_NATIVE_ANONYMOUS_ROOT != 0;
    853    }
    854 
    855    /// Whether the element is in an anonymous subtree. Note that this includes UA widgets!
    856    #[inline]
    857    fn in_native_anonymous_subtree(&self) -> bool {
    858        (self.flags() & structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) != 0
    859    }
    860 
    861    /// Whether the element has been in a UA widget
    862    #[inline]
    863    fn in_ua_widget(&self) -> bool {
    864        (self.flags() & structs::NODE_HAS_BEEN_IN_UA_WIDGET) != 0
    865    }
    866 
    867    fn css_transitions_info(&self) -> FxHashMap<OwnedPropertyDeclarationId, Arc<AnimationValue>> {
    868        use crate::gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
    869        use crate::gecko_bindings::bindings::Gecko_ElementTransitions_Length;
    870 
    871        let collection_length = unsafe { Gecko_ElementTransitions_Length(self.0) } as usize;
    872        let mut map = FxHashMap::with_capacity_and_hasher(collection_length, Default::default());
    873 
    874        for i in 0..collection_length {
    875            let end_value =
    876                unsafe { Arc::from_raw_addrefed(Gecko_ElementTransitions_EndValueAt(self.0, i)) };
    877            let property = end_value.id();
    878            debug_assert!(!property.is_logical());
    879            map.insert(property.to_owned(), end_value);
    880        }
    881        map
    882    }
    883 
    884    fn needs_transitions_update_per_property(
    885        &self,
    886        property_declaration_id: PropertyDeclarationId,
    887        combined_duration_seconds: f32,
    888        before_change_style: &ComputedValues,
    889        after_change_style: &ComputedValues,
    890        existing_transitions: &FxHashMap<OwnedPropertyDeclarationId, Arc<AnimationValue>>,
    891    ) -> bool {
    892        debug_assert!(!property_declaration_id.is_logical());
    893 
    894        // If there is an existing transition, update only if the end value
    895        // differs.
    896        //
    897        // If the end value has not changed, we should leave the currently
    898        // running transition as-is since we don't want to interrupt its timing
    899        // function.
    900        if let Some(ref existing) = existing_transitions.get(&property_declaration_id.to_owned()) {
    901            let after_value =
    902                AnimationValue::from_computed_values(property_declaration_id, after_change_style);
    903            debug_assert!(
    904                after_value.is_some()
    905                    || matches!(property_declaration_id, PropertyDeclarationId::Custom(..))
    906            );
    907            return after_value.is_none() || ***existing != after_value.unwrap();
    908        }
    909 
    910        if combined_duration_seconds <= 0.0f32 {
    911            return false;
    912        }
    913 
    914        let from =
    915            AnimationValue::from_computed_values(property_declaration_id, before_change_style);
    916        let to = AnimationValue::from_computed_values(property_declaration_id, after_change_style);
    917        debug_assert!(
    918            to.is_some() == from.is_some() ||
    919                // If the declaration contains a custom property and getComputedValue was previously
    920                // called before that custom property was defined, `from` will be `None` here.
    921                matches!(from, Some(AnimationValue::Custom(..))) ||
    922                // Similarly, if the declaration contains a custom property, getComputedValue was
    923                // previously called, and the custom property registration is removed, `to` will be
    924                // `None`.
    925                matches!(to, Some(AnimationValue::Custom(..)))
    926        );
    927 
    928        from != to
    929    }
    930 
    931    /// Get slow selector flags required for nth-of invalidation.
    932    pub fn slow_selector_flags(&self) -> ElementSelectorFlags {
    933        slow_selector_flags_from_node_selector_flags(self.as_node().selector_flags())
    934    }
    935 }
    936 
    937 /// Convert slow selector flags from the raw `NodeSelectorFlags`.
    938 pub fn slow_selector_flags_from_node_selector_flags(flags: u32) -> ElementSelectorFlags {
    939    use crate::gecko_bindings::structs::NodeSelectorFlags;
    940    let mut result = ElementSelectorFlags::empty();
    941    if flags & NodeSelectorFlags::HasSlowSelector.0 != 0 {
    942        result.insert(ElementSelectorFlags::HAS_SLOW_SELECTOR);
    943    }
    944    if flags & NodeSelectorFlags::HasSlowSelectorLaterSiblings.0 != 0 {
    945        result.insert(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS);
    946    }
    947    result
    948 }
    949 
    950 /// Converts flags from the layout used by rust-selectors to the layout used
    951 /// by Gecko. We could align these and then do this without conditionals, but
    952 /// it's probably not worth the trouble.
    953 fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
    954    use crate::gecko_bindings::structs::NodeSelectorFlags;
    955    let mut gecko_flags = 0u32;
    956    if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
    957        gecko_flags |= NodeSelectorFlags::HasSlowSelector.0;
    958    }
    959    if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
    960        gecko_flags |= NodeSelectorFlags::HasSlowSelectorLaterSiblings.0;
    961    }
    962    if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH) {
    963        gecko_flags |= NodeSelectorFlags::HasSlowSelectorNth.0;
    964    }
    965    if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) {
    966        gecko_flags |= NodeSelectorFlags::HasSlowSelectorNthOf.0;
    967    }
    968    if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
    969        gecko_flags |= NodeSelectorFlags::HasEdgeChildSelector.0;
    970    }
    971    if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) {
    972        gecko_flags |= NodeSelectorFlags::HasEmptySelector.0;
    973    }
    974    if flags.contains(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR) {
    975        gecko_flags |= NodeSelectorFlags::RelativeSelectorAnchor.0;
    976    }
    977    if flags.contains(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT) {
    978        gecko_flags |= NodeSelectorFlags::RelativeSelectorAnchorNonSubject.0;
    979    }
    980    if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR) {
    981        gecko_flags |= NodeSelectorFlags::RelativeSelectorSearchDirectionAncestor.0;
    982    }
    983    if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING) {
    984        gecko_flags |= NodeSelectorFlags::RelativeSelectorSearchDirectionSibling.0;
    985    }
    986 
    987    gecko_flags
    988 }
    989 
    990 fn get_animation_rule(
    991    element: &GeckoElement,
    992    cascade_level: CascadeLevel,
    993 ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
    994    // There's a very rough correlation between the number of effects
    995    // (animations) on an element and the number of properties it is likely to
    996    // animate, so we use that as an initial guess for the size of the
    997    // AnimationValueMap in order to reduce the number of re-allocations needed.
    998    let effect_count = unsafe { Gecko_GetAnimationEffectCount(element.0) };
    999    // Also, we should try to reuse the PDB, to avoid creating extra rule nodes.
   1000    let mut animation_values = AnimationValueMap::with_capacity_and_hasher(
   1001        effect_count.min(crate::properties::property_counts::ANIMATABLE),
   1002        Default::default(),
   1003    );
   1004    if unsafe { Gecko_GetAnimationRule(element.0, cascade_level, &mut animation_values) } {
   1005        let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
   1006        Some(Arc::new(shared_lock.wrap(
   1007            PropertyDeclarationBlock::from_animation_value_map(&animation_values),
   1008        )))
   1009    } else {
   1010        None
   1011    }
   1012 }
   1013 
   1014 /// Turns a gecko namespace id into an atom. Might panic if you pass any random thing that isn't a
   1015 /// namespace id.
   1016 #[inline(always)]
   1017 pub unsafe fn namespace_id_to_atom(id: i32) -> *mut nsAtom {
   1018    unsafe {
   1019        let namespace_manager = structs::nsNameSpaceManager_sInstance.mRawPtr;
   1020        (&(*namespace_manager).mURIArray)[id as usize].mRawPtr
   1021    }
   1022 }
   1023 
   1024 impl<'le> TElement for GeckoElement<'le> {
   1025    type ConcreteNode = GeckoNode<'le>;
   1026    type TraversalChildrenIterator = GeckoChildrenIterator<'le>;
   1027 
   1028    fn implicit_scope_for_sheet_in_shadow_root(
   1029        opaque_host: OpaqueElement,
   1030        sheet_index: usize,
   1031    ) -> Option<ImplicitScopeRoot> {
   1032        // As long as this "unopaqued" element does not escape this function, we're not leaking
   1033        // potentially-mutable elements from opaque elements.
   1034        let e = unsafe {
   1035            Self(
   1036                opaque_host
   1037                    .as_const_ptr::<RawGeckoElement>()
   1038                    .as_ref()
   1039                    .unwrap(),
   1040            )
   1041        };
   1042        let shadow_root = match e.shadow_root() {
   1043            None => return None,
   1044            Some(r) => r,
   1045        };
   1046        shadow_root.implicit_scope_for_sheet(sheet_index)
   1047    }
   1048 
   1049    fn inheritance_parent(&self) -> Option<Self> {
   1050        if let Some(pseudo) = self.implemented_pseudo_element() {
   1051            if !pseudo.is_element_backed() {
   1052                return self.pseudo_element_originating_element();
   1053            }
   1054        }
   1055 
   1056        self.as_node()
   1057            .flattened_tree_parent()
   1058            .and_then(|n| n.as_element())
   1059    }
   1060 
   1061    fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {
   1062        // This condition is similar to the check that
   1063        // StyleChildrenIterator::IsNeeded does, except that it might return
   1064        // true if we used to (but no longer) have anonymous content from
   1065        // ::before/::after, or nsIAnonymousContentCreators.
   1066        if self.is_html_slot_element()
   1067            || self.shadow_root().is_some()
   1068            || self.may_have_anonymous_children()
   1069        {
   1070            unsafe {
   1071                let mut iter = mem::MaybeUninit::<structs::StyleChildrenIterator>::uninit();
   1072                bindings::Gecko_ConstructStyleChildrenIterator(self.0, iter.as_mut_ptr());
   1073                return LayoutIterator(GeckoChildrenIterator::GeckoIterator(
   1074                    mem::ManuallyDrop::new(iter.assume_init()),
   1075                ));
   1076            }
   1077        }
   1078 
   1079        LayoutIterator(GeckoChildrenIterator::Current(self.as_node().first_child()))
   1080    }
   1081 
   1082    #[inline]
   1083    fn is_html_element(&self) -> bool {
   1084        self.namespace_id() == structs::kNameSpaceID_XHTML as i32
   1085    }
   1086 
   1087    #[inline]
   1088    fn is_mathml_element(&self) -> bool {
   1089        self.namespace_id() == structs::kNameSpaceID_MathML as i32
   1090    }
   1091 
   1092    #[inline]
   1093    fn is_svg_element(&self) -> bool {
   1094        self.namespace_id() == structs::kNameSpaceID_SVG as i32
   1095    }
   1096 
   1097    #[inline]
   1098    fn is_xul_element(&self) -> bool {
   1099        self.namespace_id() == structs::root::kNameSpaceID_XUL as i32
   1100    }
   1101 
   1102    #[inline]
   1103    fn subtree_bloom_filter(&self) -> u64 {
   1104        unsafe { bindings::Gecko_Element_GetSubtreeBloomFilter(self.0) }
   1105    }
   1106 
   1107    #[inline]
   1108    fn local_name(&self) -> &WeakAtom {
   1109        unsafe { WeakAtom::new(self.as_node().node_info().mInner.mName) }
   1110    }
   1111 
   1112    #[inline]
   1113    fn namespace(&self) -> &WeakNamespace {
   1114        unsafe { WeakNamespace::new(namespace_id_to_atom(self.namespace_id())) }
   1115    }
   1116 
   1117    #[inline]
   1118    fn query_container_size(&self, display: &Display) -> Size2D<Option<Au>> {
   1119        // If an element gets 'display: contents' and its nsIFrame has not been removed yet,
   1120        // Gecko_GetQueryContainerSize will not notice that it can't have size containment.
   1121        // Other cases like 'display: inline' will be handled once the new nsIFrame is created.
   1122        if display.is_contents() {
   1123            return Size2D::new(None, None);
   1124        }
   1125 
   1126        let mut width = -1;
   1127        let mut height = -1;
   1128        unsafe {
   1129            bindings::Gecko_GetQueryContainerSize(self.0, &mut width, &mut height);
   1130        }
   1131        Size2D::new(
   1132            if width >= 0 { Some(Au(width)) } else { None },
   1133            if height >= 0 { Some(Au(height)) } else { None },
   1134        )
   1135    }
   1136 
   1137    /// Return the list of slotted nodes of this node.
   1138    #[inline]
   1139    fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
   1140        if !self.is_html_slot_element() || !self.as_node().is_in_shadow_tree() {
   1141            return &[];
   1142        }
   1143        if cfg!(debug_assertions) {
   1144            let slot: &structs::HTMLSlotElement = unsafe { mem::transmute(self.0) };
   1145            let base: &RawGeckoElement = &slot._base._base._base;
   1146            assert_eq!(base as *const _, self.0 as *const _, "Bad cast");
   1147        }
   1148        unsafe {
   1149            let nodes = bindings::Gecko_GetAssignedNodes(self.0);
   1150            let nodes: &[*const RawGeckoNode] =
   1151                std::slice::from_raw_parts(nodes.begin, nodes.length);
   1152            // NOTE(emilio): We rely on the in-memory representation of
   1153            // GeckoNode<'ld> and *const RawGeckoNode being the same.
   1154            #[allow(dead_code)]
   1155            unsafe fn static_assert() {
   1156                mem::transmute::<*mut RawGeckoNode, GeckoNode<'static>>(0xbadc0de as *mut _);
   1157            }
   1158            mem::transmute(nodes)
   1159        }
   1160    }
   1161 
   1162    #[inline]
   1163    fn shadow_root(&self) -> Option<GeckoShadowRoot<'le>> {
   1164        let slots = self.extended_slots()?;
   1165        unsafe { slots.mShadowRoot.mRawPtr.as_ref().map(GeckoShadowRoot) }
   1166    }
   1167 
   1168    #[inline]
   1169    fn containing_shadow(&self) -> Option<GeckoShadowRoot<'le>> {
   1170        let slots = self.extended_slots()?;
   1171        unsafe {
   1172            slots
   1173                ._base
   1174                .mContainingShadow
   1175                .mRawPtr
   1176                .as_ref()
   1177                .map(GeckoShadowRoot)
   1178        }
   1179    }
   1180 
   1181    fn each_anonymous_content_child<F>(&self, mut f: F)
   1182    where
   1183        F: FnMut(Self),
   1184    {
   1185        if !self.may_have_anonymous_children() {
   1186            return;
   1187        }
   1188 
   1189        thin_vec::auto_thin_vec!(let array: [*mut nsIContent; 8]);
   1190        unsafe {
   1191            bindings::Gecko_GetAnonymousContentForElement(self.0, array.as_mut().as_mut_ptr())
   1192        };
   1193        for content in array.iter() {
   1194            let node = GeckoNode::from_content(unsafe { &**content });
   1195            let element = match node.as_element() {
   1196                Some(e) => e,
   1197                None => continue,
   1198            };
   1199            f(element);
   1200        }
   1201    }
   1202 
   1203    #[inline]
   1204    fn as_node(&self) -> Self::ConcreteNode {
   1205        unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
   1206    }
   1207 
   1208    fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
   1209        self.as_node().owner_doc().0 as *const structs::Document == device.document() as *const _
   1210    }
   1211 
   1212    fn style_attribute(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>> {
   1213        if !self.may_have_style_attribute() {
   1214            return None;
   1215        }
   1216 
   1217        unsafe {
   1218            let declarations = Gecko_GetStyleAttrDeclarationBlock(self.0).as_ref()?;
   1219            Some(ArcBorrow::from_ref(declarations))
   1220        }
   1221    }
   1222 
   1223    fn unset_dirty_style_attribute(&self) {
   1224        if !self.may_have_style_attribute() {
   1225            return;
   1226        }
   1227 
   1228        unsafe { Gecko_UnsetDirtyStyleAttr(self.0) };
   1229    }
   1230 
   1231    fn smil_override(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>> {
   1232        unsafe {
   1233            let slots = self.extended_slots()?;
   1234 
   1235            let declaration: &structs::DeclarationBlock =
   1236                slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?;
   1237 
   1238            let raw: &structs::StyleLockedDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?;
   1239            Some(ArcBorrow::from_ref(raw))
   1240        }
   1241    }
   1242 
   1243    fn animation_rule(
   1244        &self,
   1245        _: &SharedStyleContext,
   1246    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
   1247        get_animation_rule(self, CascadeLevel::Animations)
   1248    }
   1249 
   1250    fn transition_rule(
   1251        &self,
   1252        _: &SharedStyleContext,
   1253    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
   1254        get_animation_rule(self, CascadeLevel::Transitions)
   1255    }
   1256 
   1257    #[inline]
   1258    fn state(&self) -> ElementState {
   1259        ElementState::from_bits_retain(self.state_internal())
   1260    }
   1261 
   1262    #[inline]
   1263    fn has_part_attr(&self) -> bool {
   1264        self.as_node()
   1265            .get_bool_flag(nsINode_BooleanFlag::ElementHasPart)
   1266    }
   1267 
   1268    #[inline]
   1269    fn exports_any_part(&self) -> bool {
   1270        snapshot_helpers::find_attr(self.attrs(), &atom!("exportparts")).is_some()
   1271    }
   1272 
   1273    // FIXME(emilio): we should probably just return a reference to the Atom.
   1274    #[inline]
   1275    fn id(&self) -> Option<&WeakAtom> {
   1276        if !self.has_id() {
   1277            return None;
   1278        }
   1279        snapshot_helpers::get_id(self.attrs())
   1280    }
   1281 
   1282    fn each_attr_name<F>(&self, mut callback: F)
   1283    where
   1284        F: FnMut(&AtomIdent),
   1285    {
   1286        for attr in self.attrs() {
   1287            unsafe { AtomIdent::with(attr.mName.name(), |a| callback(a)) }
   1288        }
   1289    }
   1290 
   1291    fn each_class<F>(&self, callback: F)
   1292    where
   1293        F: FnMut(&AtomIdent),
   1294    {
   1295        let attr = match self.get_class_attr() {
   1296            Some(c) => c,
   1297            None => return,
   1298        };
   1299 
   1300        snapshot_helpers::each_class_or_part(attr, callback)
   1301    }
   1302 
   1303    #[inline]
   1304    fn each_custom_state<F>(&self, mut callback: F)
   1305    where
   1306        F: FnMut(&AtomIdent),
   1307    {
   1308        if let Some(slots) = self.extended_slots() {
   1309            unsafe {
   1310                for atom in slots.mCustomStates.iter() {
   1311                    AtomIdent::with(atom.mRawPtr, &mut callback)
   1312                }
   1313            }
   1314        }
   1315    }
   1316 
   1317    #[inline]
   1318    fn each_exported_part<F>(&self, name: &AtomIdent, callback: F)
   1319    where
   1320        F: FnMut(&AtomIdent),
   1321    {
   1322        snapshot_helpers::each_exported_part(self.attrs(), name, callback)
   1323    }
   1324 
   1325    fn each_part<F>(&self, callback: F)
   1326    where
   1327        F: FnMut(&AtomIdent),
   1328    {
   1329        let attr = match self.get_part_attr() {
   1330            Some(c) => c,
   1331            None => return,
   1332        };
   1333 
   1334        snapshot_helpers::each_class_or_part(attr, callback)
   1335    }
   1336 
   1337    #[inline]
   1338    fn has_snapshot(&self) -> bool {
   1339        self.flags() & ELEMENT_HAS_SNAPSHOT != 0
   1340    }
   1341 
   1342    #[inline]
   1343    fn handled_snapshot(&self) -> bool {
   1344        self.flags() & ELEMENT_HANDLED_SNAPSHOT != 0
   1345    }
   1346 
   1347    unsafe fn set_handled_snapshot(&self) {
   1348        debug_assert!(self.has_data());
   1349        self.set_flags(ELEMENT_HANDLED_SNAPSHOT)
   1350    }
   1351 
   1352    #[inline]
   1353    fn has_dirty_descendants(&self) -> bool {
   1354        self.flags() & ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO != 0
   1355    }
   1356 
   1357    unsafe fn set_dirty_descendants(&self) {
   1358        debug_assert!(self.has_data());
   1359        debug!("Setting dirty descendants: {:?}", self);
   1360        self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
   1361    }
   1362 
   1363    unsafe fn unset_dirty_descendants(&self) {
   1364        self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)
   1365    }
   1366 
   1367    #[inline]
   1368    fn has_animation_only_dirty_descendants(&self) -> bool {
   1369        self.flags() & ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO != 0
   1370    }
   1371 
   1372    unsafe fn set_animation_only_dirty_descendants(&self) {
   1373        self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
   1374    }
   1375 
   1376    unsafe fn unset_animation_only_dirty_descendants(&self) {
   1377        self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)
   1378    }
   1379 
   1380    unsafe fn clear_descendant_bits(&self) {
   1381        self.unset_flags(
   1382            ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO
   1383                | ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO
   1384                | NODE_DESCENDANTS_NEED_FRAMES,
   1385        )
   1386    }
   1387 
   1388    fn is_visited_link(&self) -> bool {
   1389        self.state().intersects(ElementState::VISITED)
   1390    }
   1391 
   1392    /// We want to match rules from the same tree in all cases, except for native anonymous content
   1393    /// that _isn't_ part directly of a UA widget (e.g., such generated by form controls, or
   1394    /// pseudo-elements).
   1395    #[inline]
   1396    fn matches_user_and_content_rules(&self) -> bool {
   1397        !self.in_native_anonymous_subtree() || self.in_ua_widget()
   1398    }
   1399 
   1400    #[inline]
   1401    fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
   1402        if !self.in_native_anonymous_subtree() {
   1403            return None;
   1404        }
   1405 
   1406        if !self.has_properties() {
   1407            return None;
   1408        }
   1409 
   1410        let name = unsafe { bindings::Gecko_GetImplementedPseudoIdentifier(self.0) };
   1411        PseudoElement::from_pseudo_type(
   1412            unsafe { bindings::Gecko_GetImplementedPseudoType(self.0) },
   1413            if name.is_null() {
   1414                None
   1415            } else {
   1416                Some(AtomIdent::new(unsafe { Atom::from_raw(name) }))
   1417            },
   1418        )
   1419    }
   1420 
   1421    #[inline]
   1422    fn store_children_to_process(&self, _: isize) {
   1423        // This is only used for bottom-up traversal, and is thus a no-op for Gecko.
   1424    }
   1425 
   1426    fn did_process_child(&self) -> isize {
   1427        panic!("Atomic child count not implemented in Gecko");
   1428    }
   1429 
   1430    unsafe fn ensure_data(&self) -> AtomicRefMut<'_, ElementData> {
   1431        if !self.has_data() {
   1432            debug!("Creating ElementData for {:?}", self);
   1433            let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::default())));
   1434            self.0.mServoData.set(ptr);
   1435        }
   1436        self.mutate_data().unwrap()
   1437    }
   1438 
   1439    unsafe fn clear_data(&self) {
   1440        let ptr = self.0.mServoData.get();
   1441        self.unset_flags(
   1442            ELEMENT_HAS_SNAPSHOT
   1443                | ELEMENT_HANDLED_SNAPSHOT
   1444                | structs::Element_kAllServoDescendantBits
   1445                | NODE_NEEDS_FRAME,
   1446        );
   1447        if !ptr.is_null() {
   1448            debug!("Dropping ElementData for {:?}", self);
   1449            let data = Box::from_raw(self.0.mServoData.get());
   1450            self.0.mServoData.set(ptr::null_mut());
   1451 
   1452            // Perform a mutable borrow of the data in debug builds. This
   1453            // serves as an assertion that there are no outstanding borrows
   1454            // when we destroy the data.
   1455            debug_assert!({
   1456                let _ = data.borrow_mut();
   1457                true
   1458            });
   1459        }
   1460    }
   1461 
   1462    #[inline]
   1463    fn skip_item_display_fixup(&self) -> bool {
   1464        debug_assert!(
   1465            !self.is_pseudo_element(),
   1466            "Just don't call me if I'm a pseudo, you should know the answer already"
   1467        );
   1468        self.is_root_of_native_anonymous_subtree()
   1469    }
   1470 
   1471    #[inline]
   1472    fn may_have_animations(&self) -> bool {
   1473        if let Some(pseudo) = self.implemented_pseudo_element() {
   1474            if pseudo.animations_stored_in_parent() {
   1475                // FIXME(emilio): When would the parent of a ::before / ::after
   1476                // pseudo-element be null?
   1477                return self.parent_element().map_or(false, |p| {
   1478                    p.as_node()
   1479                        .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
   1480                });
   1481            }
   1482        }
   1483        self.as_node()
   1484            .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
   1485    }
   1486 
   1487    /// Update various animation-related state on a given (pseudo-)element as
   1488    /// results of normal restyle.
   1489    fn update_animations(
   1490        &self,
   1491        before_change_style: Option<Arc<ComputedValues>>,
   1492        tasks: UpdateAnimationsTasks,
   1493    ) {
   1494        // We have to update animations even if the element has no computed
   1495        // style since it means the element is in a display:none subtree, we
   1496        // should destroy all CSS animations in display:none subtree.
   1497        let computed_data = self.borrow_data();
   1498        let computed_values = computed_data.as_ref().map(|d| d.styles.primary());
   1499        let before_change_values = before_change_style
   1500            .as_ref()
   1501            .map_or(ptr::null(), |x| x.as_gecko_computed_style());
   1502        let computed_values_opt = computed_values
   1503            .as_ref()
   1504            .map_or(ptr::null(), |x| x.as_gecko_computed_style());
   1505        unsafe {
   1506            Gecko_UpdateAnimations(
   1507                self.0,
   1508                before_change_values,
   1509                computed_values_opt,
   1510                tasks.bits(),
   1511            );
   1512        }
   1513    }
   1514 
   1515    #[inline]
   1516    fn has_animations(&self, _: &SharedStyleContext) -> bool {
   1517        self.has_any_animation()
   1518    }
   1519 
   1520    fn has_css_animations(&self, _: &SharedStyleContext, _: Option<PseudoElement>) -> bool {
   1521        self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) }
   1522    }
   1523 
   1524    fn has_css_transitions(&self, _: &SharedStyleContext, _: Option<PseudoElement>) -> bool {
   1525        self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }
   1526    }
   1527 
   1528    // Detect if there are any changes that require us to update transitions.
   1529    //
   1530    // This is used as a more thoroughgoing check than the cheaper
   1531    // might_need_transitions_update check.
   1532    //
   1533    // The following logic shadows the logic used on the Gecko side
   1534    // (nsTransitionManager::DoUpdateTransitions) where we actually perform the
   1535    // update.
   1536    //
   1537    // https://drafts.csswg.org/css-transitions/#starting
   1538    fn needs_transitions_update(
   1539        &self,
   1540        before_change_style: &ComputedValues,
   1541        after_change_style: &ComputedValues,
   1542    ) -> bool {
   1543        let after_change_ui_style = after_change_style.get_ui();
   1544        let existing_transitions = self.css_transitions_info();
   1545 
   1546        if after_change_style.get_box().clone_display().is_none() {
   1547            // We need to cancel existing transitions.
   1548            return !existing_transitions.is_empty();
   1549        }
   1550 
   1551        let mut transitions_to_keep = PropertyDeclarationIdSet::default();
   1552        for transition_property in after_change_style.transition_properties() {
   1553            let physical_property = transition_property
   1554                .property
   1555                .as_borrowed()
   1556                .to_physical(after_change_style.writing_mode);
   1557            transitions_to_keep.insert(physical_property);
   1558            if self.needs_transitions_update_per_property(
   1559                physical_property,
   1560                after_change_ui_style
   1561                    .transition_combined_duration_at(transition_property.index)
   1562                    .seconds(),
   1563                before_change_style,
   1564                after_change_style,
   1565                &existing_transitions,
   1566            ) {
   1567                return true;
   1568            }
   1569        }
   1570 
   1571        // Check if we have to cancel the running transition because this is not
   1572        // a matching transition-property value.
   1573        existing_transitions
   1574            .keys()
   1575            .any(|property| !transitions_to_keep.contains(property.as_borrowed()))
   1576    }
   1577 
   1578    /// Whether there is an ElementData container.
   1579    #[inline]
   1580    fn has_data(&self) -> bool {
   1581        self.get_data().is_some()
   1582    }
   1583 
   1584    /// Immutably borrows the ElementData.
   1585    fn borrow_data(&self) -> Option<AtomicRef<'_, ElementData>> {
   1586        self.get_data().map(|x| x.borrow())
   1587    }
   1588 
   1589    /// Mutably borrows the ElementData.
   1590    fn mutate_data(&self) -> Option<AtomicRefMut<'_, ElementData>> {
   1591        self.get_data().map(|x| x.borrow_mut())
   1592    }
   1593 
   1594    #[inline]
   1595    fn lang_attr(&self) -> Option<AttrValue> {
   1596        let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
   1597        if ptr.is_null() {
   1598            None
   1599        } else {
   1600            Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))
   1601        }
   1602    }
   1603 
   1604    fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool {
   1605        // Gecko supports :lang() from CSS Selectors 4, which accepts a list
   1606        // of language tags, and does BCP47-style range matching.
   1607        let override_lang_ptr = match override_lang {
   1608            Some(Some(ref atom)) => atom.as_ptr(),
   1609            _ => ptr::null_mut(),
   1610        };
   1611        value.0.iter().any(|lang| unsafe {
   1612            Gecko_MatchLang(
   1613                self.0,
   1614                override_lang_ptr,
   1615                override_lang.is_some(),
   1616                lang.as_slice().as_ptr(),
   1617            )
   1618        })
   1619    }
   1620 
   1621    fn is_html_document_body_element(&self) -> bool {
   1622        if self.local_name() != &**local_name!("body") {
   1623            return false;
   1624        }
   1625 
   1626        if !self.is_html_element() {
   1627            return false;
   1628        }
   1629 
   1630        unsafe { bindings::Gecko_IsDocumentBody(self.0) }
   1631    }
   1632 
   1633    fn synthesize_view_transition_dynamic_rules<V>(&self, rules: &mut V)
   1634    where
   1635        V: Push<ApplicableDeclarationBlock>,
   1636    {
   1637        use crate::stylesheets::layer_rule::LayerOrder;
   1638        let declarations = unsafe { bindings::Gecko_GetViewTransitionDynamicRule(self.0).as_ref() };
   1639        if let Some(decl) = declarations {
   1640            rules.push(ApplicableDeclarationBlock::from_declarations(
   1641                unsafe { Arc::from_raw_addrefed(decl) },
   1642                ServoCascadeLevel::UANormal,
   1643                LayerOrder::root(),
   1644            ));
   1645        }
   1646    }
   1647 
   1648    fn synthesize_presentational_hints_for_legacy_attributes<V>(
   1649        &self,
   1650        visited_handling: VisitedHandlingMode,
   1651        hints: &mut V,
   1652    ) where
   1653        V: Push<ApplicableDeclarationBlock>,
   1654    {
   1655        use crate::properties::longhands::_x_lang::SpecifiedValue as SpecifiedLang;
   1656        use crate::properties::longhands::color::SpecifiedValue as SpecifiedColor;
   1657        use crate::stylesheets::layer_rule::LayerOrder;
   1658        use crate::values::specified::{color::Color, font::XTextScale};
   1659        use std::sync::LazyLock;
   1660 
   1661        static TABLE_COLOR_RULE: LazyLock<ApplicableDeclarationBlock> = LazyLock::new(|| {
   1662            let global_style_data = &*GLOBAL_STYLE_DATA;
   1663            let pdb = PropertyDeclarationBlock::with_one(
   1664                PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())),
   1665                Importance::Normal,
   1666            );
   1667            let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
   1668            ApplicableDeclarationBlock::from_declarations(
   1669                arc,
   1670                ServoCascadeLevel::PresHints,
   1671                LayerOrder::root(),
   1672            )
   1673        });
   1674        static MATHML_LANG_RULE: LazyLock<ApplicableDeclarationBlock> = LazyLock::new(|| {
   1675            let global_style_data = &*GLOBAL_STYLE_DATA;
   1676            let pdb = PropertyDeclarationBlock::with_one(
   1677                PropertyDeclaration::XLang(SpecifiedLang(atom!("x-math"))),
   1678                Importance::Normal,
   1679            );
   1680            let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
   1681            ApplicableDeclarationBlock::from_declarations(
   1682                arc,
   1683                ServoCascadeLevel::PresHints,
   1684                LayerOrder::root(),
   1685            )
   1686        });
   1687        static SVG_TEXT_DISABLE_SCALE_RULE: LazyLock<ApplicableDeclarationBlock> =
   1688            LazyLock::new(|| {
   1689                let global_style_data = &*GLOBAL_STYLE_DATA;
   1690                let pdb = PropertyDeclarationBlock::with_one(
   1691                    PropertyDeclaration::XTextScale(XTextScale::None),
   1692                    Importance::Normal,
   1693                );
   1694                let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
   1695                ApplicableDeclarationBlock::from_declarations(
   1696                    arc,
   1697                    ServoCascadeLevel::PresHints,
   1698                    LayerOrder::root(),
   1699                )
   1700            });
   1701 
   1702        let ns = self.namespace_id();
   1703        // <th> elements get a default MozCenterOrInherit which may get overridden
   1704        if ns == structs::kNameSpaceID_XHTML as i32 {
   1705            if self.local_name().as_ptr() == atom!("table").as_ptr()
   1706                && self.as_node().owner_doc().quirks_mode() == QuirksMode::Quirks
   1707            {
   1708                hints.push(TABLE_COLOR_RULE.clone());
   1709            }
   1710        }
   1711        if ns == structs::kNameSpaceID_SVG as i32 {
   1712            if self.local_name().as_ptr() == atom!("text").as_ptr() {
   1713                hints.push(SVG_TEXT_DISABLE_SCALE_RULE.clone());
   1714            }
   1715        }
   1716        let declarations =
   1717            unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0).as_ref() };
   1718        if let Some(decl) = declarations {
   1719            hints.push(ApplicableDeclarationBlock::from_declarations(
   1720                unsafe { Arc::from_raw_addrefed(decl) },
   1721                ServoCascadeLevel::PresHints,
   1722                LayerOrder::root(),
   1723            ));
   1724        }
   1725        let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0).as_ref() };
   1726        if let Some(decl) = declarations {
   1727            hints.push(ApplicableDeclarationBlock::from_declarations(
   1728                unsafe { Arc::from_raw_addrefed(decl) },
   1729                ServoCascadeLevel::PresHints,
   1730                LayerOrder::root(),
   1731            ));
   1732        }
   1733 
   1734        // Support for link, vlink, and alink presentation hints on <body>
   1735        if self.is_link() {
   1736            // Unvisited vs. visited styles are computed up-front based on the
   1737            // visited mode (not the element's actual state).
   1738            let declarations = match visited_handling {
   1739                VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
   1740                    unreachable!(
   1741                        "We should never try to selector match with \
   1742                         AllLinksVisitedAndUnvisited"
   1743                    );
   1744                },
   1745                VisitedHandlingMode::AllLinksUnvisited => unsafe {
   1746                    Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0).as_ref()
   1747                },
   1748                VisitedHandlingMode::RelevantLinkVisited => unsafe {
   1749                    Gecko_GetVisitedLinkAttrDeclarationBlock(self.0).as_ref()
   1750                },
   1751            };
   1752            if let Some(decl) = declarations {
   1753                hints.push(ApplicableDeclarationBlock::from_declarations(
   1754                    unsafe { Arc::from_raw_addrefed(decl) },
   1755                    ServoCascadeLevel::PresHints,
   1756                    LayerOrder::root(),
   1757                ));
   1758            }
   1759 
   1760            let active = self
   1761                .state()
   1762                .intersects(NonTSPseudoClass::Active.state_flag());
   1763            if active {
   1764                let declarations =
   1765                    unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0).as_ref() };
   1766                if let Some(decl) = declarations {
   1767                    hints.push(ApplicableDeclarationBlock::from_declarations(
   1768                        unsafe { Arc::from_raw_addrefed(decl) },
   1769                        ServoCascadeLevel::PresHints,
   1770                        LayerOrder::root(),
   1771                    ));
   1772                }
   1773            }
   1774        }
   1775 
   1776        // xml:lang has precedence over lang, which can be
   1777        // set by Gecko_GetHTMLPresentationAttrDeclarationBlock
   1778        //
   1779        // http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language
   1780        let ptr = unsafe { bindings::Gecko_GetXMLLangValue(self.0) };
   1781        if !ptr.is_null() {
   1782            let global_style_data = &*GLOBAL_STYLE_DATA;
   1783 
   1784            let pdb = PropertyDeclarationBlock::with_one(
   1785                PropertyDeclaration::XLang(SpecifiedLang(unsafe { Atom::from_addrefed(ptr) })),
   1786                Importance::Normal,
   1787            );
   1788            let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
   1789            hints.push(ApplicableDeclarationBlock::from_declarations(
   1790                arc,
   1791                ServoCascadeLevel::PresHints,
   1792                LayerOrder::root(),
   1793            ))
   1794        }
   1795        // MathML's default lang has precedence over both `lang` and `xml:lang`
   1796        if ns == structs::kNameSpaceID_MathML as i32 {
   1797            if self.local_name().as_ptr() == atom!("math").as_ptr() {
   1798                hints.push(MATHML_LANG_RULE.clone());
   1799            }
   1800        }
   1801    }
   1802 
   1803    fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
   1804        let node_flags = selector_flags_to_node_flags(flags);
   1805        self.as_node().selector_flags() & node_flags == node_flags
   1806    }
   1807 
   1808    fn relative_selector_search_direction(&self) -> ElementSelectorFlags {
   1809        use crate::gecko_bindings::structs::NodeSelectorFlags;
   1810        let flags = self.as_node().selector_flags();
   1811        if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionAncestorSibling.0) != 0 {
   1812            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING
   1813        } else if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionAncestor.0) != 0 {
   1814            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR
   1815        } else if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionSibling.0) != 0 {
   1816            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING
   1817        } else {
   1818            ElementSelectorFlags::empty()
   1819        }
   1820    }
   1821 }
   1822 
   1823 impl<'le> AttributeProvider for GeckoElement<'le> {
   1824    fn get_attr(&self, attr: &LocalName) -> Option<String> {
   1825        //TODO(bug 2003334): Avoid unnecessary string copies/conversions here.
   1826        let mut result = nsString::new();
   1827 
   1828        if unsafe { bindings::Gecko_LookupAttrValue(self.0, attr.0.as_ptr(), &mut *result) } {
   1829            Some(result.to_string())
   1830        } else {
   1831            None
   1832        }
   1833    }
   1834 }
   1835 
   1836 impl<'le> PartialEq for GeckoElement<'le> {
   1837    #[inline]
   1838    fn eq(&self, other: &Self) -> bool {
   1839        self.0 as *const _ == other.0 as *const _
   1840    }
   1841 }
   1842 
   1843 impl<'le> Eq for GeckoElement<'le> {}
   1844 
   1845 impl<'le> Hash for GeckoElement<'le> {
   1846    #[inline]
   1847    fn hash<H: Hasher>(&self, state: &mut H) {
   1848        (self.0 as *const RawGeckoElement).hash(state);
   1849    }
   1850 }
   1851 
   1852 impl<'le> ::selectors::Element for GeckoElement<'le> {
   1853    type Impl = SelectorImpl;
   1854 
   1855    #[inline]
   1856    fn opaque(&self) -> OpaqueElement {
   1857        OpaqueElement::new(self.0)
   1858    }
   1859 
   1860    #[inline]
   1861    fn parent_element(&self) -> Option<Self> {
   1862        let parent_node = self.as_node().parent_node();
   1863        parent_node.and_then(|n| n.as_element())
   1864    }
   1865 
   1866    #[inline]
   1867    fn parent_node_is_shadow_root(&self) -> bool {
   1868        self.as_node()
   1869            .parent_node()
   1870            .map_or(false, |p| p.is_shadow_root())
   1871    }
   1872 
   1873    #[inline]
   1874    fn containing_shadow_host(&self) -> Option<Self> {
   1875        let shadow = self.containing_shadow()?;
   1876        Some(shadow.host())
   1877    }
   1878 
   1879    #[inline]
   1880    fn is_pseudo_element(&self) -> bool {
   1881        self.implemented_pseudo_element().is_some()
   1882    }
   1883 
   1884    #[inline]
   1885    fn pseudo_element_originating_element(&self) -> Option<Self> {
   1886        debug_assert!(self.is_pseudo_element());
   1887        debug_assert!(self.in_native_anonymous_subtree());
   1888        if self.in_ua_widget() {
   1889            return self.containing_shadow_host();
   1890        }
   1891        let mut current = *self;
   1892        loop {
   1893            let anon_root = current.is_root_of_native_anonymous_subtree();
   1894            current = current.traversal_parent()?;
   1895            if anon_root {
   1896                return Some(current);
   1897            }
   1898        }
   1899    }
   1900 
   1901    #[inline]
   1902    fn assigned_slot(&self) -> Option<Self> {
   1903        let slot = self.extended_slots()?._base.mAssignedSlot.mRawPtr;
   1904 
   1905        unsafe { Some(GeckoElement(&slot.as_ref()?._base._base._base)) }
   1906    }
   1907 
   1908    #[inline]
   1909    fn prev_sibling_element(&self) -> Option<Self> {
   1910        let mut sibling = self.as_node().prev_sibling();
   1911        while let Some(sibling_node) = sibling {
   1912            if let Some(el) = sibling_node.as_element() {
   1913                return Some(el);
   1914            }
   1915            sibling = sibling_node.prev_sibling();
   1916        }
   1917        None
   1918    }
   1919 
   1920    #[inline]
   1921    fn next_sibling_element(&self) -> Option<Self> {
   1922        let mut sibling = self.as_node().next_sibling();
   1923        while let Some(sibling_node) = sibling {
   1924            if let Some(el) = sibling_node.as_element() {
   1925                return Some(el);
   1926            }
   1927            sibling = sibling_node.next_sibling();
   1928        }
   1929        None
   1930    }
   1931 
   1932    #[inline]
   1933    fn first_element_child(&self) -> Option<Self> {
   1934        let mut child = self.as_node().first_child();
   1935        while let Some(child_node) = child {
   1936            if let Some(el) = child_node.as_element() {
   1937                return Some(el);
   1938            }
   1939            child = child_node.next_sibling();
   1940        }
   1941        None
   1942    }
   1943 
   1944    fn apply_selector_flags(&self, flags: ElementSelectorFlags) {
   1945        // Handle flags that apply to the element.
   1946        let self_flags = flags.for_self();
   1947        if !self_flags.is_empty() {
   1948            self.as_node()
   1949                .set_selector_flags(selector_flags_to_node_flags(flags))
   1950        }
   1951 
   1952        // Handle flags that apply to the parent.
   1953        let parent_flags = flags.for_parent();
   1954        if !parent_flags.is_empty() {
   1955            if let Some(p) = self.as_node().parent_node() {
   1956                if p.is_element() || p.is_shadow_root() {
   1957                    p.set_selector_flags(selector_flags_to_node_flags(parent_flags));
   1958                }
   1959            }
   1960        }
   1961    }
   1962 
   1963    fn has_attr_in_no_namespace(&self, local_name: &LocalName) -> bool {
   1964        for attr in self.attrs() {
   1965            if attr.mName.mBits == local_name.as_ptr() as usize {
   1966                return true;
   1967            }
   1968        }
   1969        false
   1970    }
   1971 
   1972    fn attr_matches(
   1973        &self,
   1974        ns: &NamespaceConstraint<&Namespace>,
   1975        local_name: &LocalName,
   1976        operation: &AttrSelectorOperation<&AttrValue>,
   1977    ) -> bool {
   1978        snapshot_helpers::attr_matches(self.attrs(), ns, local_name, operation)
   1979    }
   1980 
   1981    #[inline]
   1982    fn is_root(&self) -> bool {
   1983        if self
   1984            .as_node()
   1985            .get_bool_flag(nsINode_BooleanFlag::ParentIsContent)
   1986        {
   1987            return false;
   1988        }
   1989 
   1990        if !self.as_node().is_in_document() {
   1991            return false;
   1992        }
   1993 
   1994        debug_assert!(self
   1995            .as_node()
   1996            .parent_node()
   1997            .map_or(false, |p| p.is_document()));
   1998        // XXX this should always return true at this point, shouldn't it?
   1999        unsafe { bindings::Gecko_IsRootElement(self.0) }
   2000    }
   2001 
   2002    fn is_empty(&self) -> bool {
   2003        !self
   2004            .as_node()
   2005            .dom_children()
   2006            .any(|child| unsafe { Gecko_IsSignificantChild(child.0, true) })
   2007    }
   2008 
   2009    #[inline]
   2010    fn has_local_name(&self, name: &WeakAtom) -> bool {
   2011        self.local_name() == name
   2012    }
   2013 
   2014    #[inline]
   2015    fn has_namespace(&self, ns: &WeakNamespace) -> bool {
   2016        self.namespace() == ns
   2017    }
   2018 
   2019    #[inline]
   2020    fn is_same_type(&self, other: &Self) -> bool {
   2021        self.local_name() == other.local_name() && self.namespace() == other.namespace()
   2022    }
   2023 
   2024    fn match_non_ts_pseudo_class(
   2025        &self,
   2026        pseudo_class: &NonTSPseudoClass,
   2027        context: &mut MatchingContext<Self::Impl>,
   2028    ) -> bool {
   2029        use selectors::matching::*;
   2030        match *pseudo_class {
   2031            NonTSPseudoClass::Autofill
   2032            | NonTSPseudoClass::Defined
   2033            | NonTSPseudoClass::Focus
   2034            | NonTSPseudoClass::Enabled
   2035            | NonTSPseudoClass::Disabled
   2036            | NonTSPseudoClass::Checked
   2037            | NonTSPseudoClass::Fullscreen
   2038            | NonTSPseudoClass::Indeterminate
   2039            | NonTSPseudoClass::MozInert
   2040            | NonTSPseudoClass::PopoverOpen
   2041            | NonTSPseudoClass::PlaceholderShown
   2042            | NonTSPseudoClass::Target
   2043            | NonTSPseudoClass::Valid
   2044            | NonTSPseudoClass::Invalid
   2045            | NonTSPseudoClass::MozBroken
   2046            | NonTSPseudoClass::Required
   2047            | NonTSPseudoClass::Optional
   2048            | NonTSPseudoClass::ReadOnly
   2049            | NonTSPseudoClass::ReadWrite
   2050            | NonTSPseudoClass::FocusWithin
   2051            | NonTSPseudoClass::FocusVisible
   2052            | NonTSPseudoClass::MozDragOver
   2053            | NonTSPseudoClass::MozDevtoolsHighlighted
   2054            | NonTSPseudoClass::MozStyleeditorTransitioning
   2055            | NonTSPseudoClass::MozMathIncrementScriptLevel
   2056            | NonTSPseudoClass::InRange
   2057            | NonTSPseudoClass::OutOfRange
   2058            | NonTSPseudoClass::Default
   2059            | NonTSPseudoClass::UserValid
   2060            | NonTSPseudoClass::UserInvalid
   2061            | NonTSPseudoClass::MozMeterOptimum
   2062            | NonTSPseudoClass::MozMeterSubOptimum
   2063            | NonTSPseudoClass::MozMeterSubSubOptimum
   2064            | NonTSPseudoClass::MozHasDirAttr
   2065            | NonTSPseudoClass::MozDirAttrLTR
   2066            | NonTSPseudoClass::MozDirAttrRTL
   2067            | NonTSPseudoClass::MozDirAttrLikeAuto
   2068            | NonTSPseudoClass::Modal
   2069            | NonTSPseudoClass::MozTopmostModal
   2070            | NonTSPseudoClass::Open
   2071            | NonTSPseudoClass::Active
   2072            | NonTSPseudoClass::Hover
   2073            | NonTSPseudoClass::HasSlotted
   2074            | NonTSPseudoClass::MozAutofillPreview
   2075            | NonTSPseudoClass::MozRevealed
   2076            | NonTSPseudoClass::ActiveViewTransition
   2077            | NonTSPseudoClass::MozValueEmpty
   2078            | NonTSPseudoClass::MozSuppressForPrintSelection => {
   2079                self.state().intersects(pseudo_class.state_flag())
   2080            },
   2081            NonTSPseudoClass::Dir(ref dir) => self.state().intersects(dir.element_state()),
   2082            NonTSPseudoClass::ActiveViewTransitionType(ref types) => {
   2083                self.state().intersects(pseudo_class.state_flag())
   2084                    && unsafe {
   2085                        bindings::Gecko_HasActiveViewTransitionTypes(
   2086                            self.as_node().owner_doc().0,
   2087                            types,
   2088                        )
   2089                    }
   2090            },
   2091            NonTSPseudoClass::AnyLink => self.is_link(),
   2092            NonTSPseudoClass::Link => {
   2093                self.is_link() && context.visited_handling().matches_unvisited()
   2094            },
   2095            NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),
   2096            NonTSPseudoClass::Visited => {
   2097                self.is_link() && context.visited_handling().matches_visited()
   2098            },
   2099            NonTSPseudoClass::MozFirstNode => {
   2100                if context.needs_selector_flags() {
   2101                    self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
   2102                }
   2103                let mut elem = self.as_node();
   2104                while let Some(prev) = elem.prev_sibling() {
   2105                    if prev.contains_non_whitespace_content() {
   2106                        return false;
   2107                    }
   2108                    elem = prev;
   2109                }
   2110                true
   2111            },
   2112            NonTSPseudoClass::MozLastNode => {
   2113                if context.needs_selector_flags() {
   2114                    self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
   2115                }
   2116                let mut elem = self.as_node();
   2117                while let Some(next) = elem.next_sibling() {
   2118                    if next.contains_non_whitespace_content() {
   2119                        return false;
   2120                    }
   2121                    elem = next;
   2122                }
   2123                true
   2124            },
   2125            NonTSPseudoClass::MozOnlyWhitespace => {
   2126                if context.needs_selector_flags() {
   2127                    self.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR);
   2128                }
   2129                if self
   2130                    .as_node()
   2131                    .dom_children()
   2132                    .any(|c| c.contains_non_whitespace_content())
   2133                {
   2134                    return false;
   2135                }
   2136                true
   2137            },
   2138            NonTSPseudoClass::MozNativeAnonymous => !self.matches_user_and_content_rules(),
   2139            NonTSPseudoClass::MozTableBorderNonzero => unsafe {
   2140                bindings::Gecko_IsTableBorderNonzero(self.0)
   2141            },
   2142            NonTSPseudoClass::MozSelectListBox => unsafe {
   2143                bindings::Gecko_IsSelectListBox(self.0)
   2144            },
   2145            NonTSPseudoClass::MozIsHTML => self.as_node().owner_doc().is_html_document(),
   2146            NonTSPseudoClass::MozLocaleDir(..) | NonTSPseudoClass::MozWindowInactive => {
   2147                let state_bit = pseudo_class.document_state_flag();
   2148                if state_bit.is_empty() {
   2149                    debug_assert!(
   2150                        matches!(pseudo_class, NonTSPseudoClass::MozLocaleDir(..)),
   2151                        "Only moz-locale-dir should ever return an empty state"
   2152                    );
   2153                    return false;
   2154                }
   2155                if context
   2156                    .extra_data
   2157                    .invalidation_data
   2158                    .document_state
   2159                    .intersects(state_bit)
   2160                {
   2161                    return !context.in_negation();
   2162                }
   2163                self.document_state().contains(state_bit)
   2164            },
   2165            NonTSPseudoClass::MozPlaceholder => false,
   2166            NonTSPseudoClass::Lang(ref lang_arg) => self.match_element_lang(None, lang_arg),
   2167            NonTSPseudoClass::Heading(ref levels) => levels.matches_state(self.state()),
   2168        }
   2169    }
   2170 
   2171    fn match_pseudo_element(
   2172        &self,
   2173        pseudo_selector: &PseudoElement,
   2174        _context: &mut MatchingContext<Self::Impl>,
   2175    ) -> bool {
   2176        // TODO(emilio): I believe we could assert we are a pseudo-element and
   2177        // match the proper pseudo-element, given how we rulehash the stuff
   2178        // based on the pseudo.
   2179        let pseudo = match self.implemented_pseudo_element() {
   2180            Some(pseudo) => pseudo,
   2181            None => return false,
   2182        };
   2183 
   2184        pseudo.matches(pseudo_selector, self)
   2185    }
   2186 
   2187    #[inline]
   2188    fn is_link(&self) -> bool {
   2189        self.state().intersects(ElementState::VISITED_OR_UNVISITED)
   2190    }
   2191 
   2192    #[inline]
   2193    fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
   2194        if !self.has_id() {
   2195            return false;
   2196        }
   2197 
   2198        let element_id = match snapshot_helpers::get_id(self.attrs()) {
   2199            Some(id) => id,
   2200            None => return false,
   2201        };
   2202 
   2203        case_sensitivity.eq_atom(element_id, id)
   2204    }
   2205 
   2206    #[inline]
   2207    fn is_part(&self, name: &AtomIdent) -> bool {
   2208        let attr = match self.get_part_attr() {
   2209            Some(c) => c,
   2210            None => return false,
   2211        };
   2212 
   2213        snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
   2214    }
   2215 
   2216    #[inline]
   2217    fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
   2218        snapshot_helpers::imported_part(self.attrs(), name)
   2219    }
   2220 
   2221    #[inline(always)]
   2222    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
   2223        let attr = match self.get_class_attr() {
   2224            Some(c) => c,
   2225            None => return false,
   2226        };
   2227 
   2228        snapshot_helpers::has_class_or_part(name, case_sensitivity, attr)
   2229    }
   2230 
   2231    #[inline]
   2232    fn has_custom_state(&self, state: &AtomIdent) -> bool {
   2233        if !self.is_html_element() {
   2234            return false;
   2235        }
   2236        let check_state_ptr: *const nsAtom = state.as_ptr();
   2237        self.extended_slots().map_or(false, |slot| {
   2238            (&slot.mCustomStates).iter().any(|setstate| {
   2239                let setstate_ptr: *const nsAtom = setstate.mRawPtr;
   2240                setstate_ptr == check_state_ptr
   2241            })
   2242        })
   2243    }
   2244 
   2245    #[inline]
   2246    fn is_html_element_in_html_document(&self) -> bool {
   2247        self.is_html_element() && self.as_node().owner_doc().is_html_document()
   2248    }
   2249 
   2250    #[inline]
   2251    fn is_html_slot_element(&self) -> bool {
   2252        self.is_html_element() && self.local_name().as_ptr() == local_name!("slot").as_ptr()
   2253    }
   2254 
   2255    #[inline]
   2256    fn ignores_nth_child_selectors(&self) -> bool {
   2257        self.is_root_of_native_anonymous_subtree()
   2258    }
   2259 
   2260    fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {
   2261        each_relevant_element_hash(*self, |hash| filter.insert_hash(hash & BLOOM_HASH_MASK));
   2262        true
   2263    }
   2264 }