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