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 http://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 CaseSensitivityExt;
18 use app_units::Au;
19 use applicable_declarations::ApplicableDeclarationBlock;
20 use atomic_refcell::{AtomicRefCell, AtomicRefMut};
21 use author_styles::AuthorStyles;
22 use context::{QuirksMode, SharedStyleContext, PostAnimationTasks, UpdateAnimationsTasks};
23 use data::ElementData;
24 use dom::{LayoutIterator, NodeInfo, OpaqueNode, TElement, TDocument, TNode, TShadowRoot};
25 use element_state::{ElementState, DocumentState};
26 use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
27 use gecko::data::GeckoStyleSheet;
28 use gecko::global_style_data::GLOBAL_STYLE_DATA;
29 use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement};
30 use gecko::snapshot_helpers;
31 use gecko_bindings::bindings;
32 use gecko_bindings::bindings::{Gecko_ConstructStyleChildrenIterator, Gecko_DestroyStyleChildrenIterator};
33 use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentLWTheme};
34 use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetNextStyleChild};
35 use gecko_bindings::bindings::{Gecko_IsRootElement, Gecko_MatchesElement};
36 use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
37 use gecko_bindings::bindings::Gecko_ClassOrClassList;
38 use gecko_bindings::bindings::Gecko_ElementHasAnimations;
39 use gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
40 use gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
41 use gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
42 use gecko_bindings::bindings::Gecko_GetAnimationRule;
43 use gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;
44 use gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;
45 use gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock;
46 use gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock;
47 use gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock;
48 use gecko_bindings::bindings::Gecko_IsSignificantChild;
49 use gecko_bindings::bindings::Gecko_MatchLang;
50 use gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr;
51 use gecko_bindings::bindings::Gecko_UpdateAnimations;
52 use gecko_bindings::structs;
53 use gecko_bindings::structs::{RawGeckoElement, RawGeckoNode, RawGeckoXBLBinding};
54 use gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag};
55 use gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
56 use gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
57 use gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
58 use gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
59 use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
60 use gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES;
61 use gecko_bindings::structs::NODE_NEEDS_FRAME;
62 use gecko_bindings::structs::nsChangeHint;
63 use gecko_bindings::structs::nsIDocument_DocumentTheme as DocumentTheme;
64 use gecko_bindings::structs::nsRestyleHint;
65 use gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI};
66 use hash::FnvHashMap;
67 use logical_geometry::WritingMode;
68 use media_queries::Device;
69 use properties::{ComputedValues, LonghandId};
70 use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
71 use properties::animated_properties::{AnimationValue, AnimationValueMap};
72 use properties::animated_properties::TransitionProperty;
73 use properties::style_structs::Font;
74 use rule_tree::CascadeLevel as ServoCascadeLevel;
75 use selector_parser::{AttrValue, Direction, PseudoClassStringArg};
76 use selectors::{Element, OpaqueElement};
77 use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint};
78 use selectors::matching::{ElementSelectorFlags, MatchingContext};
79 use selectors::matching::VisitedHandlingMode;
80 use selectors::sink::Push;
81 use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
82 use shared_lock::Locked;
83 use std::cell::RefCell;
84 use std::fmt;
85 use std::hash::{Hash, Hasher};
86 use std::mem;
87 use std::ptr;
88 use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
89 use stylist::CascadeData;
90 
91 /// A simple wrapper over `nsIDocument`.
92 #[derive(Clone, Copy)]
93 pub struct GeckoDocument<'ld>(pub &'ld structs::nsIDocument);
94 
95 impl<'ld> TDocument for GeckoDocument<'ld> {
96     type ConcreteNode = GeckoNode<'ld>;
97 
98     #[inline]
as_node(&self) -> Self::ConcreteNode99     fn as_node(&self) -> Self::ConcreteNode {
100         GeckoNode(&self.0._base)
101     }
102 
103     #[inline]
is_html_document(&self) -> bool104     fn is_html_document(&self) -> bool {
105         self.0.mType == structs::root::nsIDocument_Type::eHTML
106     }
107 
108     #[inline]
quirks_mode(&self) -> QuirksMode109     fn quirks_mode(&self) -> QuirksMode {
110         self.0.mCompatMode.into()
111     }
112 
elements_with_id(&self, id: &Atom) -> Result<&[GeckoElement<'ld>], ()>113     fn elements_with_id(&self, id: &Atom) -> Result<&[GeckoElement<'ld>], ()> {
114         unsafe {
115             let array = bindings::Gecko_GetElementsWithId(self.0, id.as_ptr());
116             if array.is_null() {
117                 return Ok(&[]);
118             }
119 
120             let elements: &[*mut RawGeckoElement] = &**array;
121 
122             // NOTE(emilio): We rely on the in-memory representation of
123             // GeckoElement<'ld> and *mut RawGeckoElement being the same.
124             #[allow(dead_code)]
125             unsafe fn static_assert() {
126                 mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _);
127             }
128 
129             Ok(mem::transmute(elements))
130         }
131     }
132 }
133 
134 /// A simple wrapper over `ShadowRoot`.
135 #[derive(Clone, Copy)]
136 pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
137 
138 impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
139     type ConcreteNode = GeckoNode<'lr>;
140 
141     #[inline]
as_node(&self) -> Self::ConcreteNode142     fn as_node(&self) -> Self::ConcreteNode {
143         GeckoNode(&self.0._base._base._base._base)
144     }
145 
146     #[inline]
host(&self) -> GeckoElement<'lr>147     fn host(&self) -> GeckoElement<'lr> {
148         GeckoElement(unsafe { &*self.0._base.mHost.mRawPtr })
149     }
150 
151     #[inline]
style_data<'a>(&self) -> &'a CascadeData where Self: 'a,152     fn style_data<'a>(&self) -> &'a CascadeData
153     where
154         Self: 'a,
155     {
156         debug_assert!(!self.0.mServoStyles.mPtr.is_null());
157 
158         let author_styles = unsafe {
159             &*(self.0.mServoStyles.mPtr
160                 as *const structs::RawServoAuthorStyles
161                 as *const bindings::RawServoAuthorStyles)
162         };
163 
164         let author_styles =
165             AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles);
166 
167         debug_assert!(
168             author_styles.quirks_mode == self.as_node().owner_doc().quirks_mode() ||
169             author_styles.stylesheets.is_empty()
170         );
171 
172         &author_styles.data
173     }
174 }
175 
176 /// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
177 ///
178 /// Important: We don't currently refcount the DOM, because the wrapper lifetime
179 /// magic guarantees that our LayoutFoo references won't outlive the root, and
180 /// we don't mutate any of the references on the Gecko side during restyle.
181 ///
182 /// We could implement refcounting if need be (at a potentially non-trivial
183 /// performance cost) by implementing Drop and making LayoutFoo non-Copy.
184 #[derive(Clone, Copy)]
185 pub struct GeckoNode<'ln>(pub &'ln RawGeckoNode);
186 
187 impl<'ln> PartialEq for GeckoNode<'ln> {
188     #[inline]
eq(&self, other: &Self) -> bool189     fn eq(&self, other: &Self) -> bool {
190         self.0 as *const _ == other.0 as *const _
191     }
192 }
193 
194 impl<'ln> fmt::Debug for GeckoNode<'ln> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result195     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196         if let Some(el) = self.as_element() {
197             return el.fmt(f)
198         }
199 
200         if self.is_text_node() {
201             return write!(f, "<text node> ({:#x})", self.opaque().0)
202         }
203 
204         if self.is_document() {
205             return write!(f, "<document> ({:#x})", self.opaque().0)
206         }
207 
208         if self.is_shadow_root() {
209             return write!(f, "<shadow-root> ({:#x})", self.opaque().0)
210         }
211 
212         write!(f, "<non-text node> ({:#x})", self.opaque().0)
213     }
214 }
215 
216 impl<'ln> GeckoNode<'ln> {
217     #[inline]
is_document(&self) -> bool218     fn is_document(&self) -> bool {
219         // This is a DOM constant that isn't going to change.
220         const DOCUMENT_NODE: u16 = 9;
221         self.node_info().mInner.mNodeType == DOCUMENT_NODE
222     }
223 
224     #[inline]
is_shadow_root(&self) -> bool225     fn is_shadow_root(&self) -> bool {
226         self.is_in_shadow_tree() && self.parent_node().is_none()
227     }
228 
229     #[inline]
from_content(content: &'ln nsIContent) -> Self230     fn from_content(content: &'ln nsIContent) -> Self {
231         GeckoNode(&content._base)
232     }
233 
234     #[inline]
flags(&self) -> u32235     fn flags(&self) -> u32 {
236         (self.0)._base._base_1.mFlags
237     }
238 
239     #[inline]
node_info(&self) -> &structs::NodeInfo240     fn node_info(&self) -> &structs::NodeInfo {
241         debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null());
242         unsafe { &*self.0.mNodeInfo.mRawPtr }
243     }
244 
245     // These live in different locations depending on processor architecture.
246     #[cfg(target_pointer_width = "64")]
247     #[inline]
bool_flags(&self) -> u32248     fn bool_flags(&self) -> u32 {
249         (self.0)._base._base_1.mBoolFlags
250     }
251 
252     #[cfg(target_pointer_width = "32")]
253     #[inline]
bool_flags(&self) -> u32254     fn bool_flags(&self) -> u32 {
255         (self.0).mBoolFlags
256     }
257 
258     #[inline]
get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool259     fn get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool {
260         self.bool_flags() & (1u32 << flag as u32) != 0
261     }
262 
263     /// This logic is duplicate in Gecko's nsINode::IsInShadowTree().
264     #[inline]
is_in_shadow_tree(&self) -> bool265     fn is_in_shadow_tree(&self) -> bool {
266         use gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;
267         self.flags() & (NODE_IS_IN_SHADOW_TREE as u32) != 0
268     }
269 
270     /// WARNING: This logic is duplicated in Gecko's FlattenedTreeParentIsParent.
271     /// Make sure to mirror any modifications in both places.
272     #[inline]
flattened_tree_parent_is_parent(&self) -> bool273     fn flattened_tree_parent_is_parent(&self) -> bool {
274         use gecko_bindings::structs::*;
275         let flags = self.flags();
276         if flags & (NODE_MAY_BE_IN_BINDING_MNGR as u32 |
277                     NODE_IS_IN_SHADOW_TREE as u32) != 0 {
278             return false;
279         }
280 
281         let parent = unsafe { self.0.mParent.as_ref() }.map(GeckoNode);
282         let parent_el = parent.and_then(|p| p.as_element());
283         if flags & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0 &&
284            parent_el.map_or(false, |el| el.is_root())
285         {
286             return false;
287         }
288 
289         if let Some(parent) = parent_el {
290             if parent.shadow_root().is_some() || parent.xbl_binding().is_some() {
291                 return false;
292             }
293         }
294 
295         true
296     }
297 
298     #[inline]
flattened_tree_parent(&self) -> Option<Self>299     fn flattened_tree_parent(&self) -> Option<Self> {
300         // TODO(emilio): Measure and consider not doing this fast-path and take
301         // always the common path, it's only a function call and from profiles
302         // it seems that keeping this fast path makes the compiler not inline
303         // `flattened_tree_parent`.
304         if self.flattened_tree_parent_is_parent() {
305             debug_assert_eq!(
306                 unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) },
307                 self.parent_node(),
308                 "Fast path stopped holding!"
309             );
310 
311             return self.parent_node();
312         }
313 
314         // NOTE(emilio): If this call is too expensive, we could manually
315         // inline more aggressively.
316         unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) }
317     }
318 
319     #[inline]
contains_non_whitespace_content(&self) -> bool320     fn contains_non_whitespace_content(&self) -> bool {
321         unsafe { Gecko_IsSignificantChild(self.0, false) }
322     }
323 }
324 
325 impl<'ln> NodeInfo for GeckoNode<'ln> {
326     #[inline]
is_element(&self) -> bool327     fn is_element(&self) -> bool {
328         self.get_bool_flag(nsINode_BooleanFlag::NodeIsElement)
329     }
330 
is_text_node(&self) -> bool331     fn is_text_node(&self) -> bool {
332         // This is a DOM constant that isn't going to change.
333         const TEXT_NODE: u16 = 3;
334         self.node_info().mInner.mNodeType == TEXT_NODE
335     }
336 }
337 
338 impl<'ln> TNode for GeckoNode<'ln> {
339     type ConcreteDocument = GeckoDocument<'ln>;
340     type ConcreteShadowRoot = GeckoShadowRoot<'ln>;
341     type ConcreteElement = GeckoElement<'ln>;
342 
343     #[inline]
parent_node(&self) -> Option<Self>344     fn parent_node(&self) -> Option<Self> {
345         unsafe { self.0.mParent.as_ref().map(GeckoNode) }
346     }
347 
348     #[inline]
first_child(&self) -> Option<Self>349     fn first_child(&self) -> Option<Self> {
350         unsafe { self.0.mFirstChild.as_ref().map(GeckoNode::from_content) }
351     }
352 
353     #[inline]
last_child(&self) -> Option<Self>354     fn last_child(&self) -> Option<Self> {
355         unsafe { Gecko_GetLastChild(self.0).map(GeckoNode) }
356     }
357 
358     #[inline]
prev_sibling(&self) -> Option<Self>359     fn prev_sibling(&self) -> Option<Self> {
360         unsafe { self.0.mPreviousSibling.as_ref().map(GeckoNode::from_content) }
361     }
362 
363     #[inline]
next_sibling(&self) -> Option<Self>364     fn next_sibling(&self) -> Option<Self> {
365         unsafe { self.0.mNextSibling.as_ref().map(GeckoNode::from_content) }
366     }
367 
368     #[inline]
owner_doc(&self) -> Self::ConcreteDocument369     fn owner_doc(&self) -> Self::ConcreteDocument {
370         debug_assert!(!self.node_info().mDocument.is_null());
371         GeckoDocument(unsafe { &*self.node_info().mDocument })
372     }
373 
374     #[inline]
is_in_document(&self) -> bool375     fn is_in_document(&self) -> bool {
376         self.get_bool_flag(nsINode_BooleanFlag::IsInDocument)
377     }
378 
traversal_parent(&self) -> Option<GeckoElement<'ln>>379     fn traversal_parent(&self) -> Option<GeckoElement<'ln>> {
380         self.flattened_tree_parent().and_then(|n| n.as_element())
381     }
382 
383     #[inline]
opaque(&self) -> OpaqueNode384     fn opaque(&self) -> OpaqueNode {
385         let ptr: usize = self.0 as *const _ as usize;
386         OpaqueNode(ptr)
387     }
388 
debug_id(self) -> usize389     fn debug_id(self) -> usize {
390         unimplemented!()
391     }
392 
393     #[inline]
as_element(&self) -> Option<GeckoElement<'ln>>394     fn as_element(&self) -> Option<GeckoElement<'ln>> {
395         if !self.is_element() {
396             return None;
397         }
398 
399         Some(GeckoElement(unsafe {
400             &*(self.0 as *const _ as *const RawGeckoElement)
401         }))
402     }
403 
404     #[inline]
as_document(&self) -> Option<Self::ConcreteDocument>405     fn as_document(&self) -> Option<Self::ConcreteDocument> {
406         if !self.is_document() {
407             return None;
408         }
409 
410         debug_assert_eq!(self.owner_doc().as_node(), *self, "How?");
411         Some(self.owner_doc())
412     }
413 
414     #[inline]
as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot>415     fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {
416         if !self.is_shadow_root() {
417             return None;
418         }
419 
420         Some(GeckoShadowRoot(unsafe {
421             &*(self.0 as *const _ as *const structs::ShadowRoot)
422         }))
423     }
424 }
425 
426 /// A wrapper on top of two kind of iterators, depending on the parent being
427 /// iterated.
428 ///
429 /// We generally iterate children by traversing the light-tree siblings of the
430 /// first child like Servo does.
431 ///
432 /// However, for nodes with anonymous children, we use a custom (heavier-weight)
433 /// Gecko-implemented iterator.
434 ///
435 /// FIXME(emilio): If we take into account shadow DOM, we're going to need the
436 /// flat tree pretty much always. We can try to optimize the case where there's
437 /// no shadow root sibling, probably.
438 pub enum GeckoChildrenIterator<'a> {
439     /// A simple iterator that tracks the current node being iterated and
440     /// replaces it with the next sibling when requested.
441     Current(Option<GeckoNode<'a>>),
442     /// A Gecko-implemented iterator we need to drop appropriately.
443     GeckoIterator(structs::StyleChildrenIterator),
444 }
445 
446 impl<'a> Drop for GeckoChildrenIterator<'a> {
drop(&mut self)447     fn drop(&mut self) {
448         if let GeckoChildrenIterator::GeckoIterator(ref mut it) = *self {
449             unsafe {
450                 Gecko_DestroyStyleChildrenIterator(it);
451             }
452         }
453     }
454 }
455 
456 impl<'a> Iterator for GeckoChildrenIterator<'a> {
457     type Item = GeckoNode<'a>;
next(&mut self) -> Option<GeckoNode<'a>>458     fn next(&mut self) -> Option<GeckoNode<'a>> {
459         match *self {
460             GeckoChildrenIterator::Current(curr) => {
461                 let next = curr.and_then(|node| node.next_sibling());
462                 *self = GeckoChildrenIterator::Current(next);
463                 curr
464             },
465             GeckoChildrenIterator::GeckoIterator(ref mut it) => unsafe {
466                 // We do this unsafe lengthening of the lifetime here because
467                 // structs::StyleChildrenIterator is actually StyleChildrenIterator<'a>,
468                 // however we can't express this easily with bindgen, and it would
469                 // introduce functions with two input lifetimes into bindgen,
470                 // which would be out of scope for elision.
471                 Gecko_GetNextStyleChild(&mut * (it as *mut _)).map(GeckoNode)
472             }
473         }
474     }
475 }
476 
477 /// A Simple wrapper over a non-null Gecko `nsXBLBinding` pointer.
478 #[derive(Clone, Copy)]
479 pub struct GeckoXBLBinding<'lb>(pub &'lb RawGeckoXBLBinding);
480 
481 impl<'lb> GeckoXBLBinding<'lb> {
482     #[inline]
base_binding(&self) -> Option<Self>483     fn base_binding(&self) -> Option<Self> {
484         unsafe { self.0.mNextBinding.mRawPtr.as_ref().map(GeckoXBLBinding) }
485     }
486 
487     #[inline]
anon_content(&self) -> *const nsIContent488     fn anon_content(&self) -> *const nsIContent {
489         self.0.mContent.raw::<nsIContent>()
490     }
491 
492     #[inline]
inherits_style(&self) -> bool493     fn inherits_style(&self) -> bool {
494         unsafe { bindings::Gecko_XBLBinding_InheritsStyle(self.0) }
495     }
496 
497     // This duplicates the logic in Gecko's
498     // nsBindingManager::GetBindingWithContent.
binding_with_content(&self) -> Option<Self>499     fn binding_with_content(&self) -> Option<Self> {
500         let mut binding = *self;
501         loop {
502             if !binding.anon_content().is_null() {
503                 return Some(binding);
504             }
505             binding = binding.base_binding()?;
506         }
507     }
508 
each_xbl_cascade_data<F>(&self, f: &mut F) where F: FnMut(&'lb CascadeData, QuirksMode),509     fn each_xbl_cascade_data<F>(&self, f: &mut F)
510     where
511         F: FnMut(&'lb CascadeData, QuirksMode),
512     {
513         if let Some(base) = self.base_binding() {
514             base.each_xbl_cascade_data(f);
515         }
516 
517         let data = unsafe {
518             bindings::Gecko_XBLBinding_GetRawServoStyles(self.0)
519         };
520 
521         if let Some(data) = data {
522             let data: &'lb _ = AuthorStyles::<GeckoStyleSheet>::from_ffi(data);
523             f(&data.data, data.quirks_mode)
524         }
525     }
526 }
527 
528 /// A simple wrapper over a non-null Gecko `Element` pointer.
529 #[derive(Clone, Copy)]
530 pub struct GeckoElement<'le>(pub &'le RawGeckoElement);
531 
532 impl<'le> fmt::Debug for GeckoElement<'le> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result533     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
534         write!(f, "<{}", self.local_name())?;
535         if let Some(id) = self.id() {
536             write!(f, " id={}", id)?;
537         }
538 
539         let mut first = true;
540         let mut any = false;
541         self.each_class(|c| {
542             if first {
543                 first = false;
544                 any = true;
545                 let _ = f.write_str(" class=\"");
546             } else {
547                 let _ = f.write_str(" ");
548             }
549             let _ = write!(f, "{}", c);
550         });
551 
552         if any {
553             f.write_str("\"")?;
554         }
555 
556         write!(f, "> ({:#x})", self.as_node().opaque().0)
557     }
558 }
559 
560 impl<'le> GeckoElement<'le> {
561     #[inline]
may_have_anonymous_children(&self) -> bool562     fn may_have_anonymous_children(&self) -> bool {
563         self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementMayHaveAnonymousChildren)
564     }
565 
566     #[inline]
flags(&self) -> u32567     fn flags(&self) -> u32 {
568         self.as_node().flags()
569     }
570 
571     // FIXME: We can implement this without OOL calls, but we can't easily given
572     // GeckoNode is a raw reference.
573     //
574     // We can use a Cell<T>, but that's a bit of a pain.
set_flags(&self, flags: u32)575     fn set_flags(&self, flags: u32) {
576         unsafe { Gecko_SetNodeFlags(self.as_node().0, flags) }
577     }
578 
unset_flags(&self, flags: u32)579     unsafe fn unset_flags(&self, flags: u32) {
580         Gecko_UnsetNodeFlags(self.as_node().0, flags)
581     }
582 
583     /// Returns true if this element has descendants for lazy frame construction.
descendants_need_frames(&self) -> bool584     pub fn descendants_need_frames(&self) -> bool {
585         self.flags() & (NODE_DESCENDANTS_NEED_FRAMES as u32) != 0
586     }
587 
588     /// Returns true if this element needs lazy frame construction.
needs_frame(&self) -> bool589     pub fn needs_frame(&self) -> bool {
590         self.flags() & (NODE_NEEDS_FRAME as u32) != 0
591     }
592 
593     /// Returns a reference to the DOM slots for this Element, if they exist.
dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots>594     fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> {
595         let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots;
596         unsafe { slots.as_ref() }
597     }
598 
599     /// Returns a reference to the extended DOM slots for this Element.
extended_slots( &self, ) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots>600     fn extended_slots(
601         &self,
602     ) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {
603         self.dom_slots().and_then(|s| unsafe {
604             (s._base.mExtendedSlots.mPtr as *const structs::FragmentOrElement_nsExtendedDOMSlots).as_ref()
605         })
606     }
607 
608     #[inline]
may_be_in_binding_manager(&self) -> bool609     fn may_be_in_binding_manager(&self) -> bool {
610         self.flags() & (structs::NODE_MAY_BE_IN_BINDING_MNGR as u32) != 0
611     }
612 
613     #[inline]
xbl_binding(&self) -> Option<GeckoXBLBinding<'le>>614     fn xbl_binding(&self) -> Option<GeckoXBLBinding<'le>> {
615         if !self.may_be_in_binding_manager() {
616             return None;
617         }
618 
619         let slots = self.extended_slots()?;
620         unsafe { slots.mXBLBinding.mRawPtr.as_ref().map(GeckoXBLBinding) }
621     }
622 
623     #[inline]
xbl_binding_with_content(&self) -> Option<GeckoXBLBinding<'le>>624     fn xbl_binding_with_content(&self) -> Option<GeckoXBLBinding<'le>> {
625         self.xbl_binding().and_then(|b| b.binding_with_content())
626     }
627 
628     #[inline]
has_xbl_binding_with_content(&self) -> bool629     fn has_xbl_binding_with_content(&self) -> bool {
630         !self.xbl_binding_with_content().is_none()
631     }
632 
633     /// This and has_xbl_binding_parent duplicate the logic in Gecko's virtual
634     /// nsINode::GetBindingParent function, which only has two implementations:
635     /// one for XUL elements, and one for other elements.  We just hard code in
636     /// our knowledge of those two implementations here.
xbl_binding_parent(&self) -> Option<Self>637     fn xbl_binding_parent(&self) -> Option<Self> {
638         if self.is_xul_element() {
639             // FIXME(heycam): Having trouble with bindgen on nsXULElement,
640             // where the binding parent is stored in a member variable
641             // rather than in slots.  So just get it through FFI for now.
642             unsafe {
643                 bindings::Gecko_GetBindingParent(self.0).map(GeckoElement)
644             }
645         } else {
646             let binding_parent = unsafe {
647                 self.non_xul_xbl_binding_parent_raw_content().as_ref()
648             }.map(GeckoNode::from_content).and_then(|n| n.as_element());
649 
650             debug_assert!(binding_parent == unsafe {
651                 bindings::Gecko_GetBindingParent(self.0).map(GeckoElement)
652             });
653             binding_parent
654         }
655     }
656 
non_xul_xbl_binding_parent_raw_content(&self) -> *mut nsIContent657     fn non_xul_xbl_binding_parent_raw_content(&self) -> *mut nsIContent {
658         debug_assert!(!self.is_xul_element());
659         self.extended_slots()
660             .map_or(ptr::null_mut(), |slots| slots._base.mBindingParent)
661     }
662 
has_xbl_binding_parent(&self) -> bool663     fn has_xbl_binding_parent(&self) -> bool {
664         if self.is_xul_element() {
665             // FIXME(heycam): Having trouble with bindgen on nsXULElement,
666             // where the binding parent is stored in a member variable
667             // rather than in slots.  So just get it through FFI for now.
668             unsafe { bindings::Gecko_GetBindingParent(self.0).is_some() }
669         } else {
670             !self.non_xul_xbl_binding_parent_raw_content().is_null()
671         }
672     }
673 
674     #[inline]
namespace_id(&self) -> i32675     fn namespace_id(&self) -> i32 {
676         self.as_node().node_info().mInner.mNamespaceID
677     }
678 
679     #[inline]
is_xul_element(&self) -> bool680     fn is_xul_element(&self) -> bool {
681         self.namespace_id() == (structs::root::kNameSpaceID_XUL as i32)
682     }
683 
684     #[inline]
has_id(&self) -> bool685     fn has_id(&self) -> bool {
686         self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasID)
687     }
688 
689     #[inline]
state_internal(&self) -> u64690     fn state_internal(&self) -> u64 {
691         if !self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasLockedStyleStates) {
692             return self.0.mState.mStates;
693         }
694         unsafe { Gecko_ElementState(self.0) }
695     }
696 
697     #[inline]
document_state(&self) -> DocumentState698     fn document_state(&self) -> DocumentState {
699         DocumentState::from_bits_truncate(
700             self.as_node().owner_doc().0.mDocumentState.mStates
701         )
702     }
703 
704     #[inline]
may_have_class(&self) -> bool705     fn may_have_class(&self) -> bool {
706         self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementMayHaveClass)
707     }
708 
709     #[inline]
has_properties(&self) -> bool710     fn has_properties(&self) -> bool {
711         use gecko_bindings::structs::NODE_HAS_PROPERTIES;
712 
713         (self.flags() & NODE_HAS_PROPERTIES as u32) != 0
714     }
715 
716     #[inline]
before_or_after_pseudo(&self, is_before: bool) -> Option<Self>717     fn before_or_after_pseudo(&self, is_before: bool) -> Option<Self> {
718         if !self.has_properties() {
719             return None;
720         }
721 
722         unsafe { bindings::Gecko_GetBeforeOrAfterPseudo(self.0, is_before).map(GeckoElement) }
723     }
724 
725     #[inline]
may_have_style_attribute(&self) -> bool726     fn may_have_style_attribute(&self) -> bool {
727         self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementMayHaveStyle)
728     }
729 
730     #[inline]
document_theme(&self) -> DocumentTheme731     fn document_theme(&self) -> DocumentTheme {
732         let node = self.as_node();
733         unsafe { Gecko_GetDocumentLWTheme(node.owner_doc().0) }
734     }
735 
736     /// Only safe to call on the main thread, with exclusive access to the
737     /// element and its ancestors.
738     ///
739     /// This function is also called after display property changed for SMIL
740     /// animation.
741     ///
742     /// Also this function schedules style flush.
note_explicit_hints( &self, restyle_hint: nsRestyleHint, change_hint: nsChangeHint, )743     pub unsafe fn note_explicit_hints(
744         &self,
745         restyle_hint: nsRestyleHint,
746         change_hint: nsChangeHint,
747     ) {
748         use gecko::restyle_damage::GeckoRestyleDamage;
749         use invalidation::element::restyle_hints::RestyleHint;
750 
751         let damage = GeckoRestyleDamage::new(change_hint);
752         debug!("note_explicit_hints: {:?}, restyle_hint={:?}, change_hint={:?}",
753                self, restyle_hint, change_hint);
754 
755         let restyle_hint: RestyleHint = restyle_hint.into();
756         debug_assert!(!(restyle_hint.has_animation_hint() &&
757                         restyle_hint.has_non_animation_hint()),
758                       "Animation restyle hints should not appear with non-animation restyle hints");
759 
760         let mut data = match self.mutate_data() {
761             Some(d) => d,
762             None => {
763                 debug!("(Element not styled, discarding hints)");
764                 return;
765             }
766         };
767 
768         debug_assert!(data.has_styles(), "how?");
769 
770         // Propagate the bit up the chain.
771         if restyle_hint.has_animation_hint() {
772             bindings::Gecko_NoteAnimationOnlyDirtyElement(self.0);
773         } else {
774             bindings::Gecko_NoteDirtyElement(self.0);
775         }
776 
777         data.hint.insert(restyle_hint);
778         data.damage |= damage;
779     }
780 
781     /// This logic is duplicated in Gecko's nsIContent::IsRootOfAnonymousSubtree.
782     #[inline]
is_root_of_anonymous_subtree(&self) -> bool783     fn is_root_of_anonymous_subtree(&self) -> bool {
784         use gecko_bindings::structs::NODE_IS_ANONYMOUS_ROOT;
785         self.flags() & (NODE_IS_ANONYMOUS_ROOT as u32) != 0
786     }
787 
788     /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree.
789     #[inline]
is_root_of_native_anonymous_subtree(&self) -> bool790     fn is_root_of_native_anonymous_subtree(&self) -> bool {
791         use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT;
792         return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0
793     }
794 
795     /// This logic is duplicated in Gecko's nsINode::IsInNativeAnonymousSubtree.
796     #[inline]
is_in_native_anonymous_subtree(&self) -> bool797     fn is_in_native_anonymous_subtree(&self) -> bool {
798         use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
799         self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
800     }
801 
802     /// This logic is duplicated in Gecko's nsIContent::IsInAnonymousSubtree.
803     #[inline]
is_in_anonymous_subtree(&self) -> bool804     fn is_in_anonymous_subtree(&self) -> bool {
805         self.is_in_native_anonymous_subtree() ||
806         (!self.as_node().is_in_shadow_tree() && self.has_xbl_binding_parent())
807     }
808 
css_transitions_info(&self) -> FnvHashMap<LonghandId, Arc<AnimationValue>>809     fn css_transitions_info(&self) -> FnvHashMap<LonghandId, Arc<AnimationValue>> {
810         use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;
811         use gecko_bindings::bindings::Gecko_ElementTransitions_Length;
812 
813         let collection_length =
814             unsafe { Gecko_ElementTransitions_Length(self.0) } as usize;
815         let mut map = FnvHashMap::with_capacity_and_hasher(
816             collection_length,
817             Default::default()
818         );
819 
820         for i in 0..collection_length {
821             let raw_end_value = unsafe {
822                  Gecko_ElementTransitions_EndValueAt(self.0, i)
823             };
824 
825             let end_value = AnimationValue::arc_from_borrowed(&raw_end_value)
826                 .expect("AnimationValue not found in ElementTransitions");
827 
828             let property = end_value.id();
829             map.insert(property, end_value.clone_arc());
830         }
831         map
832     }
833 
needs_transitions_update_per_property( &self, longhand_id: LonghandId, combined_duration: f32, before_change_style: &ComputedValues, after_change_style: &ComputedValues, existing_transitions: &FnvHashMap<LonghandId, Arc<AnimationValue>>, ) -> bool834     fn needs_transitions_update_per_property(
835         &self,
836         longhand_id: LonghandId,
837         combined_duration: f32,
838         before_change_style: &ComputedValues,
839         after_change_style: &ComputedValues,
840         existing_transitions: &FnvHashMap<LonghandId, Arc<AnimationValue>>,
841     ) -> bool {
842         use values::animated::{Animate, Procedure};
843 
844         // If there is an existing transition, update only if the end value
845         // differs.
846         //
847         // If the end value has not changed, we should leave the currently
848         // running transition as-is since we don't want to interrupt its timing
849         // function.
850         if let Some(ref existing) = existing_transitions.get(&longhand_id) {
851             let after_value =
852                 AnimationValue::from_computed_values(
853                     longhand_id,
854                     after_change_style
855                 ).unwrap();
856 
857             return ***existing != after_value
858         }
859 
860         let from = AnimationValue::from_computed_values(
861             longhand_id,
862             before_change_style,
863         );
864         let to = AnimationValue::from_computed_values(
865             longhand_id,
866             after_change_style,
867         );
868 
869         debug_assert_eq!(to.is_some(), from.is_some());
870 
871         combined_duration > 0.0f32 &&
872         from != to &&
873         from.unwrap().animate(
874             to.as_ref().unwrap(),
875             Procedure::Interpolate { progress: 0.5 }
876         ).is_ok()
877     }
878 }
879 
880 /// Converts flags from the layout used by rust-selectors to the layout used
881 /// by Gecko. We could align these and then do this without conditionals, but
882 /// it's probably not worth the trouble.
selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32883 fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
884     use gecko_bindings::structs::*;
885     let mut gecko_flags = 0u32;
886     if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
887         gecko_flags |= NODE_HAS_SLOW_SELECTOR as u32;
888     }
889     if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
890         gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS as u32;
891     }
892     if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
893         gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR as u32;
894     }
895     if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) {
896         gecko_flags |= NODE_HAS_EMPTY_SELECTOR as u32;
897     }
898 
899     gecko_flags
900 }
901 
get_animation_rule( element: &GeckoElement, cascade_level: CascadeLevel, ) -> Option<Arc<Locked<PropertyDeclarationBlock>>>902 fn get_animation_rule(
903     element: &GeckoElement,
904     cascade_level: CascadeLevel,
905 ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
906     use gecko_bindings::sugar::ownership::HasSimpleFFI;
907     // Also, we should try to reuse the PDB, to avoid creating extra rule nodes.
908     let mut animation_values = AnimationValueMap::default();
909     if unsafe { Gecko_GetAnimationRule(element.0,
910                                        cascade_level,
911                                        AnimationValueMap::as_ffi_mut(&mut animation_values)) } {
912         let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;
913         Some(Arc::new(shared_lock.wrap(
914             PropertyDeclarationBlock::from_animation_value_map(&animation_values))))
915     } else {
916         None
917     }
918 }
919 
920 #[derive(Debug)]
921 /// Gecko font metrics provider
922 pub struct GeckoFontMetricsProvider {
923     /// Cache of base font sizes for each language
924     ///
925     /// Usually will have 1 element.
926     ///
927     // This may be slow on pages using more languages, might be worth optimizing
928     // by caching lang->group mapping separately and/or using a hashmap on larger
929     // loads.
930     pub font_size_cache: RefCell<Vec<(Atom, ::gecko_bindings::structs::FontSizePrefs)>>,
931 }
932 
933 impl GeckoFontMetricsProvider {
934     /// Construct
new() -> Self935     pub fn new() -> Self {
936         GeckoFontMetricsProvider {
937             font_size_cache: RefCell::new(Vec::new()),
938         }
939     }
940 }
941 
942 impl FontMetricsProvider for GeckoFontMetricsProvider {
create_from(_: &SharedStyleContext) -> GeckoFontMetricsProvider943     fn create_from(_: &SharedStyleContext) -> GeckoFontMetricsProvider {
944         GeckoFontMetricsProvider::new()
945     }
946 
get_size(&self, font_name: &Atom, font_family: u8) -> Au947     fn get_size(&self, font_name: &Atom, font_family: u8) -> Au {
948         use gecko_bindings::bindings::Gecko_GetBaseSize;
949         let mut cache = self.font_size_cache.borrow_mut();
950         if let Some(sizes) = cache.iter().find(|el| el.0 == *font_name) {
951             return sizes.1.size_for_generic(font_family);
952         }
953         let sizes = unsafe { Gecko_GetBaseSize(font_name.as_ptr()) };
954         cache.push((font_name.clone(), sizes));
955         sizes.size_for_generic(font_family)
956     }
957 
query( &self, font: &Font, font_size: Au, wm: WritingMode, in_media_query: bool, device: &Device, ) -> FontMetricsQueryResult958     fn query(
959         &self,
960         font: &Font,
961         font_size: Au,
962         wm: WritingMode,
963         in_media_query: bool,
964         device: &Device,
965     ) -> FontMetricsQueryResult {
966         use gecko_bindings::bindings::Gecko_GetFontMetrics;
967         let gecko_metrics = unsafe {
968             Gecko_GetFontMetrics(
969                 device.pres_context(),
970                 wm.is_vertical() && !wm.is_sideways(),
971                 font.gecko(),
972                 font_size.0,
973                 // we don't use the user font set in a media query
974                 !in_media_query,
975             )
976         };
977         let metrics = FontMetrics {
978             x_height: Au(gecko_metrics.mXSize),
979             zero_advance_measure: Au(gecko_metrics.mChSize),
980         };
981         FontMetricsQueryResult::Available(metrics)
982     }
983 }
984 
985 impl structs::FontSizePrefs {
size_for_generic(&self, font_family: u8) -> Au986     fn size_for_generic(&self, font_family: u8) -> Au {
987         Au(match font_family {
988             structs::kPresContext_DefaultVariableFont_ID => self.mDefaultVariableSize,
989             structs::kPresContext_DefaultFixedFont_ID => self.mDefaultFixedSize,
990             structs::kGenericFont_serif => self.mDefaultSerifSize,
991             structs::kGenericFont_sans_serif => self.mDefaultSansSerifSize,
992             structs::kGenericFont_monospace => self.mDefaultMonospaceSize,
993             structs::kGenericFont_cursive => self.mDefaultCursiveSize,
994             structs::kGenericFont_fantasy => self.mDefaultFantasySize,
995             _ => unreachable!("Unknown generic ID"),
996         })
997     }
998 }
999 
1000 impl<'le> TElement for GeckoElement<'le> {
1001     type ConcreteNode = GeckoNode<'le>;
1002     type FontMetricsProvider = GeckoFontMetricsProvider;
1003     type TraversalChildrenIterator = GeckoChildrenIterator<'le>;
1004 
inheritance_parent(&self) -> Option<Self>1005     fn inheritance_parent(&self) -> Option<Self> {
1006         if self.is_native_anonymous() {
1007             self.closest_non_native_anonymous_ancestor()
1008         } else {
1009             self.as_node()
1010                 .flattened_tree_parent()
1011                 .and_then(|n| n.as_element())
1012         }
1013     }
1014 
traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>>1015     fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {
1016         // This condition is similar to the check that
1017         // StyleChildrenIterator::IsNeeded does, except that it might return
1018         // true if we used to (but no longer) have anonymous content from
1019         // ::before/::after, XBL bindings, or nsIAnonymousContentCreators.
1020         if self.is_in_anonymous_subtree() ||
1021            self.has_xbl_binding_with_content() ||
1022            self.is_html_slot_element() ||
1023            self.shadow_root().is_some() ||
1024            self.may_have_anonymous_children() {
1025             unsafe {
1026                 let mut iter: structs::StyleChildrenIterator = ::std::mem::zeroed();
1027                 Gecko_ConstructStyleChildrenIterator(self.0, &mut iter);
1028                 return LayoutIterator(GeckoChildrenIterator::GeckoIterator(iter));
1029             }
1030         }
1031 
1032         LayoutIterator(GeckoChildrenIterator::Current(self.as_node().first_child()))
1033     }
1034 
before_pseudo_element(&self) -> Option<Self>1035     fn before_pseudo_element(&self) -> Option<Self> {
1036         self.before_or_after_pseudo(/* is_before = */ true)
1037     }
1038 
after_pseudo_element(&self) -> Option<Self>1039     fn after_pseudo_element(&self) -> Option<Self> {
1040         self.before_or_after_pseudo(/* is_before = */ false)
1041     }
1042 
1043     /// Ensure this accurately represents the rules that an element may ever
1044     /// match, even in the native anonymous content case.
style_scope(&self) -> Self::ConcreteNode1045     fn style_scope(&self) -> Self::ConcreteNode {
1046         if self.implemented_pseudo_element().is_some() {
1047             return self.closest_non_native_anonymous_ancestor().unwrap().style_scope();
1048         }
1049 
1050         if self.is_in_native_anonymous_subtree() {
1051             return self.as_node().owner_doc().as_node();
1052         }
1053 
1054         if self.xbl_binding().is_some() || self.shadow_root().is_some() {
1055             return self.as_node();
1056         }
1057 
1058         if let Some(parent) = self.xbl_binding_parent() {
1059             return parent.as_node();
1060         }
1061 
1062         self.as_node().owner_doc().as_node()
1063     }
1064 
1065 
1066     #[inline]
is_html_element(&self) -> bool1067     fn is_html_element(&self) -> bool {
1068         self.namespace_id() == (structs::root::kNameSpaceID_XHTML as i32)
1069     }
1070 
1071     /// Return the list of slotted nodes of this node.
1072     #[inline]
slotted_nodes(&self) -> &[Self::ConcreteNode]1073     fn slotted_nodes(&self) -> &[Self::ConcreteNode] {
1074         if !self.is_html_slot_element() || !self.as_node().is_in_shadow_tree() {
1075             return &[];
1076         }
1077 
1078         let slot: &structs::HTMLSlotElement = unsafe {
1079             mem::transmute(self.0)
1080         };
1081 
1082         if cfg!(debug_assertions) {
1083             let base: &RawGeckoElement = &slot._base._base._base._base;
1084             assert_eq!(base as *const _, self.0 as *const _, "Bad cast");
1085         }
1086 
1087         let assigned_nodes: &[structs::RefPtr<structs::nsINode>] =
1088             &*slot.mAssignedNodes;
1089 
1090         debug_assert_eq!(
1091             mem::size_of::<structs::RefPtr<structs::nsINode>>(),
1092             mem::size_of::<Self::ConcreteNode>(),
1093             "Bad cast!"
1094         );
1095 
1096         unsafe { mem::transmute(assigned_nodes) }
1097     }
1098 
1099     #[inline]
shadow_root(&self) -> Option<GeckoShadowRoot<'le>>1100     fn shadow_root(&self) -> Option<GeckoShadowRoot<'le>> {
1101         let slots = self.extended_slots()?;
1102         unsafe { slots.mShadowRoot.mRawPtr.as_ref().map(GeckoShadowRoot) }
1103     }
1104 
1105     #[inline]
containing_shadow(&self) -> Option<GeckoShadowRoot<'le>>1106     fn containing_shadow(&self) -> Option<GeckoShadowRoot<'le>> {
1107         let slots = self.extended_slots()?;
1108         unsafe { slots._base.mContainingShadow.mRawPtr.as_ref().map(GeckoShadowRoot) }
1109     }
1110 
1111     /// Execute `f` for each anonymous content child element (apart from
1112     /// ::before and ::after) whose originating element is `self`.
each_anonymous_content_child<F>(&self, mut f: F) where F: FnMut(Self),1113     fn each_anonymous_content_child<F>(&self, mut f: F)
1114     where
1115         F: FnMut(Self),
1116     {
1117         let array: *mut structs::nsTArray<*mut nsIContent> =
1118             unsafe { bindings::Gecko_GetAnonymousContentForElement(self.0) };
1119 
1120         if array.is_null() {
1121             return;
1122         }
1123 
1124         for content in unsafe { &**array } {
1125             let node = GeckoNode::from_content(unsafe { &**content });
1126             let element = match node.as_element() {
1127                 Some(e) => e,
1128                 None => continue,
1129             };
1130 
1131             f(element);
1132         }
1133 
1134         unsafe { bindings::Gecko_DestroyAnonymousContentList(array) };
1135     }
1136 
closest_non_native_anonymous_ancestor(&self) -> Option<Self>1137     fn closest_non_native_anonymous_ancestor(&self) -> Option<Self> {
1138         debug_assert!(self.is_native_anonymous());
1139         let mut parent = self.traversal_parent()?;
1140 
1141         loop {
1142             if !parent.is_native_anonymous() {
1143                 return Some(parent);
1144             }
1145 
1146             parent = parent.traversal_parent()?;
1147         }
1148     }
1149 
1150     #[inline]
as_node(&self) -> Self::ConcreteNode1151     fn as_node(&self) -> Self::ConcreteNode {
1152         unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
1153     }
1154 
owner_doc_matches_for_testing(&self, device: &Device) -> bool1155     fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
1156         self.as_node().owner_doc().0 as *const structs::nsIDocument ==
1157             device.pres_context().mDocument.raw::<structs::nsIDocument>()
1158     }
1159 
style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>1160     fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
1161         if !self.may_have_style_attribute() {
1162             return None;
1163         }
1164 
1165         let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0) };
1166         let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
1167             declarations.and_then(|s| s.as_arc_opt());
1168         declarations.map(|s| s.borrow_arc())
1169     }
1170 
unset_dirty_style_attribute(&self)1171     fn unset_dirty_style_attribute(&self) {
1172         if !self.may_have_style_attribute() {
1173             return;
1174         }
1175 
1176         unsafe { Gecko_UnsetDirtyStyleAttr(self.0) };
1177     }
1178 
smil_override(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>1179     fn smil_override(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
1180         unsafe {
1181             let slots = self.extended_slots()?;
1182 
1183             let base_declaration: &structs::DeclarationBlock =
1184                 slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?;
1185 
1186             assert_eq!(base_declaration.mType, structs::StyleBackendType_Servo);
1187             let declaration: &structs::ServoDeclarationBlock =
1188                 mem::transmute(base_declaration);
1189 
1190             debug_assert_eq!(
1191                 &declaration._base as *const structs::DeclarationBlock,
1192                 base_declaration as *const structs::DeclarationBlock
1193             );
1194 
1195             let raw: &structs::RawServoDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?;
1196 
1197             Some(Locked::<PropertyDeclarationBlock>::as_arc(
1198                 &*(&raw as *const &structs::RawServoDeclarationBlock)
1199             ).borrow_arc())
1200         }
1201     }
1202 
animation_rule(&self) -> Option<Arc<Locked<PropertyDeclarationBlock>>>1203     fn animation_rule(&self) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
1204         get_animation_rule(self, CascadeLevel::Animations)
1205     }
1206 
transition_rule(&self) -> Option<Arc<Locked<PropertyDeclarationBlock>>>1207     fn transition_rule(&self) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
1208         get_animation_rule(self, CascadeLevel::Transitions)
1209     }
1210 
1211     #[inline]
state(&self) -> ElementState1212     fn state(&self) -> ElementState {
1213         ElementState::from_bits_truncate(self.state_internal())
1214     }
1215 
1216     #[inline]
has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool1217     fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool {
1218         unsafe {
1219             bindings::Gecko_HasAttr(self.0, namespace.0.as_ptr(), attr.as_ptr())
1220         }
1221     }
1222 
1223     // FIXME(emilio): we should probably just return a reference to the Atom.
1224     #[inline]
id(&self) -> Option<&WeakAtom>1225     fn id(&self) -> Option<&WeakAtom> {
1226         if !self.has_id() {
1227             return None;
1228         }
1229 
1230         let ptr = unsafe {
1231             bindings::Gecko_AtomAttrValue(self.0, atom!("id").as_ptr())
1232         };
1233 
1234         // FIXME(emilio): Pretty sure the has_id flag is exact and we could
1235         // assert here.
1236         if ptr.is_null() {
1237             None
1238         } else {
1239             Some(unsafe { WeakAtom::new(ptr) })
1240         }
1241     }
1242 
each_class<F>(&self, callback: F) where F: FnMut(&Atom),1243     fn each_class<F>(&self, callback: F)
1244     where
1245         F: FnMut(&Atom),
1246     {
1247         if !self.may_have_class() {
1248             return;
1249         }
1250 
1251         snapshot_helpers::each_class(self.0, callback, Gecko_ClassOrClassList)
1252     }
1253 
1254     #[inline]
has_snapshot(&self) -> bool1255     fn has_snapshot(&self) -> bool {
1256         self.flags() & (ELEMENT_HAS_SNAPSHOT as u32) != 0
1257     }
1258 
1259     #[inline]
handled_snapshot(&self) -> bool1260     fn handled_snapshot(&self) -> bool {
1261         self.flags() & (ELEMENT_HANDLED_SNAPSHOT as u32) != 0
1262     }
1263 
set_handled_snapshot(&self)1264     unsafe fn set_handled_snapshot(&self) {
1265         debug_assert!(self.get_data().is_some());
1266         self.set_flags(ELEMENT_HANDLED_SNAPSHOT as u32)
1267     }
1268 
1269     #[inline]
has_dirty_descendants(&self) -> bool1270     fn has_dirty_descendants(&self) -> bool {
1271         self.flags() & (ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
1272     }
1273 
set_dirty_descendants(&self)1274     unsafe fn set_dirty_descendants(&self) {
1275         debug_assert!(self.get_data().is_some());
1276         debug!("Setting dirty descendants: {:?}", self);
1277         self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
1278     }
1279 
unset_dirty_descendants(&self)1280     unsafe fn unset_dirty_descendants(&self) {
1281         self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32)
1282     }
1283 
1284     #[inline]
has_animation_only_dirty_descendants(&self) -> bool1285     fn has_animation_only_dirty_descendants(&self) -> bool {
1286         self.flags() & (ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
1287     }
1288 
set_animation_only_dirty_descendants(&self)1289     unsafe fn set_animation_only_dirty_descendants(&self) {
1290         self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32)
1291     }
1292 
unset_animation_only_dirty_descendants(&self)1293     unsafe fn unset_animation_only_dirty_descendants(&self) {
1294         self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32)
1295     }
1296 
clear_descendant_bits(&self)1297     unsafe fn clear_descendant_bits(&self) {
1298         self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
1299                          ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
1300                          NODE_DESCENDANTS_NEED_FRAMES as u32)
1301     }
1302 
1303     #[inline]
clear_dirty_bits(&self)1304     unsafe fn clear_dirty_bits(&self) {
1305         self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
1306                          ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO as u32 |
1307                          NODE_DESCENDANTS_NEED_FRAMES as u32 |
1308                          NODE_NEEDS_FRAME as u32)
1309     }
1310 
is_visited_link(&self) -> bool1311     fn is_visited_link(&self) -> bool {
1312         self.state().intersects(ElementState::IN_VISITED_STATE)
1313     }
1314 
1315     #[inline]
is_native_anonymous(&self) -> bool1316     fn is_native_anonymous(&self) -> bool {
1317         use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
1318         self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0
1319     }
1320 
1321     #[inline]
matches_user_and_author_rules(&self) -> bool1322     fn matches_user_and_author_rules(&self) -> bool {
1323         !self.is_in_native_anonymous_subtree()
1324     }
1325 
implemented_pseudo_element(&self) -> Option<PseudoElement>1326     fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
1327         if !self.is_native_anonymous() {
1328             return None;
1329         }
1330 
1331         if !self.has_properties() {
1332             return None;
1333         }
1334 
1335         let pseudo_type =
1336             unsafe { bindings::Gecko_GetImplementedPseudo(self.0) };
1337         PseudoElement::from_pseudo_type(pseudo_type)
1338     }
1339 
store_children_to_process(&self, _: isize)1340     fn store_children_to_process(&self, _: isize) {
1341         // This is only used for bottom-up traversal, and is thus a no-op for Gecko.
1342     }
1343 
did_process_child(&self) -> isize1344     fn did_process_child(&self) -> isize {
1345         panic!("Atomic child count not implemented in Gecko");
1346     }
1347 
1348     #[inline(always)]
get_data(&self) -> Option<&AtomicRefCell<ElementData>>1349     fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
1350         unsafe { self.0.mServoData.get().as_ref() }
1351     }
1352 
ensure_data(&self) -> AtomicRefMut<ElementData>1353     unsafe fn ensure_data(&self) -> AtomicRefMut<ElementData> {
1354         if self.get_data().is_none() {
1355             debug!("Creating ElementData for {:?}", self);
1356             let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::default())));
1357             self.0.mServoData.set(ptr);
1358         }
1359         self.mutate_data().unwrap()
1360     }
1361 
clear_data(&self)1362     unsafe fn clear_data(&self) {
1363         let ptr = self.0.mServoData.get();
1364         self.unset_flags(ELEMENT_HAS_SNAPSHOT as u32 |
1365                          ELEMENT_HANDLED_SNAPSHOT as u32 |
1366                          structs::Element_kAllServoDescendantBits |
1367                          NODE_NEEDS_FRAME as u32);
1368         if !ptr.is_null() {
1369             debug!("Dropping ElementData for {:?}", self);
1370             let data = Box::from_raw(self.0.mServoData.get());
1371             self.0.mServoData.set(ptr::null_mut());
1372 
1373             // Perform a mutable borrow of the data in debug builds. This
1374             // serves as an assertion that there are no outstanding borrows
1375             // when we destroy the data.
1376             debug_assert!({ let _ = data.borrow_mut(); true });
1377         }
1378     }
1379 
1380     #[inline]
skip_item_display_fixup(&self) -> bool1381     fn skip_item_display_fixup(&self) -> bool {
1382         debug_assert!(
1383             self.implemented_pseudo_element().is_none(),
1384             "Just don't call me if I'm a pseudo, you should know the answer already"
1385         );
1386         self.is_root_of_native_anonymous_subtree()
1387     }
1388 
set_selector_flags(&self, flags: ElementSelectorFlags)1389     unsafe fn set_selector_flags(&self, flags: ElementSelectorFlags) {
1390         debug_assert!(!flags.is_empty());
1391         self.set_flags(selector_flags_to_node_flags(flags));
1392     }
1393 
has_selector_flags(&self, flags: ElementSelectorFlags) -> bool1394     fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
1395         let node_flags = selector_flags_to_node_flags(flags);
1396         (self.flags() & node_flags) == node_flags
1397     }
1398 
1399     #[inline]
may_have_animations(&self) -> bool1400     fn may_have_animations(&self) -> bool {
1401         if let Some(pseudo) = self.implemented_pseudo_element() {
1402             if !pseudo.is_before_or_after() {
1403                 return false;
1404             }
1405             // FIXME(emilio): When would the parent of a ::before / ::after
1406             // pseudo-element be null?
1407             return self.parent_element().map_or(false, |p| {
1408                p.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
1409             });
1410         }
1411         self.as_node().get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
1412     }
1413 
1414     /// Process various tasks that are a result of animation-only restyle.
process_post_animation(&self, tasks: PostAnimationTasks)1415     fn process_post_animation(&self, tasks: PostAnimationTasks) {
1416         use gecko_bindings::structs::nsChangeHint_nsChangeHint_Empty;
1417         use gecko_bindings::structs::nsRestyleHint_eRestyle_Subtree;
1418 
1419         debug_assert!(!tasks.is_empty(), "Should be involved a task");
1420 
1421         // If display style was changed from none to other, we need to resolve
1422         // the descendants in the display:none subtree. Instead of resolving
1423         // those styles in animation-only restyle, we defer it to a subsequent
1424         // normal restyle.
1425         if tasks.intersects(PostAnimationTasks::DISPLAY_CHANGED_FROM_NONE_FOR_SMIL) {
1426             debug_assert!(self.implemented_pseudo_element()
1427                               .map_or(true, |p| !p.is_before_or_after()),
1428                           "display property animation shouldn't run on pseudo elements \
1429                            since it's only for SMIL");
1430             unsafe {
1431                 self.note_explicit_hints(
1432                     nsRestyleHint_eRestyle_Subtree,
1433                     nsChangeHint_nsChangeHint_Empty,
1434                 );
1435             }
1436         }
1437     }
1438 
1439     /// Update various animation-related state on a given (pseudo-)element as
1440     /// results of normal restyle.
update_animations(&self, before_change_style: Option<Arc<ComputedValues>>, tasks: UpdateAnimationsTasks)1441     fn update_animations(&self,
1442                          before_change_style: Option<Arc<ComputedValues>>,
1443                          tasks: UpdateAnimationsTasks) {
1444         // We have to update animations even if the element has no computed
1445         // style since it means the element is in a display:none subtree, we
1446         // should destroy all CSS animations in display:none subtree.
1447         let computed_data = self.borrow_data();
1448         let computed_values =
1449             computed_data.as_ref().map(|d| d.styles.primary());
1450         let before_change_values =
1451             before_change_style.as_ref().map(|x| &**x);
1452         let computed_values_opt = computed_values.as_ref().map(|x| &***x);
1453         unsafe {
1454             Gecko_UpdateAnimations(self.0,
1455                                    before_change_values,
1456                                    computed_values_opt,
1457                                    tasks.bits());
1458         }
1459     }
1460 
has_animations(&self) -> bool1461     fn has_animations(&self) -> bool {
1462         self.may_have_animations() && unsafe { Gecko_ElementHasAnimations(self.0) }
1463     }
1464 
has_css_animations(&self) -> bool1465     fn has_css_animations(&self) -> bool {
1466         self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) }
1467     }
1468 
has_css_transitions(&self) -> bool1469     fn has_css_transitions(&self) -> bool {
1470         self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }
1471     }
1472 
each_xbl_cascade_data<'a, F>(&self, mut f: F) -> bool where 'le: 'a, F: FnMut(&'a CascadeData, QuirksMode),1473     fn each_xbl_cascade_data<'a, F>(&self, mut f: F) -> bool
1474     where
1475         'le: 'a,
1476         F: FnMut(&'a CascadeData, QuirksMode),
1477     {
1478         // Walk the binding scope chain, starting with the binding attached to
1479         // our content, up till we run out of scopes or we get cut off.
1480         //
1481         // If we are a NAC pseudo-element, we want to get rules from our
1482         // rule_hash_target, that is, our originating element.
1483         let mut current = Some(self.rule_hash_target());
1484         while let Some(element) = current {
1485             if let Some(binding) = element.xbl_binding() {
1486                 binding.each_xbl_cascade_data(&mut f);
1487 
1488                 // If we're not looking at our original element, allow the
1489                 // binding to cut off style inheritance.
1490                 if element != *self && !binding.inherits_style() {
1491                     // Go no further; we're not inheriting style from
1492                     // anything above here.
1493                     break;
1494                 }
1495             }
1496 
1497             if element.is_root_of_native_anonymous_subtree() {
1498                 // Deliberately cut off style inheritance here.
1499                 break;
1500             }
1501 
1502             current = element.xbl_binding_parent();
1503         }
1504 
1505         // If current has something, this means we cut off inheritance at some
1506         // point in the loop.
1507         current.is_some()
1508     }
1509 
xbl_binding_anonymous_content(&self) -> Option<GeckoNode<'le>>1510     fn xbl_binding_anonymous_content(&self) -> Option<GeckoNode<'le>> {
1511         self.xbl_binding_with_content()
1512             .map(|b| unsafe { GeckoNode::from_content(&*b.anon_content()) })
1513     }
1514 
might_need_transitions_update( &self, old_values: Option<&ComputedValues>, new_values: &ComputedValues, ) -> bool1515     fn might_need_transitions_update(
1516         &self,
1517         old_values: Option<&ComputedValues>,
1518         new_values: &ComputedValues,
1519     ) -> bool {
1520         use properties::longhands::display::computed_value::T as Display;
1521 
1522         let old_values = match old_values {
1523             Some(v) => v,
1524             None => return false,
1525         };
1526 
1527         let new_box_style = new_values.get_box();
1528         let transition_not_running = !self.has_css_transitions() &&
1529                                      new_box_style.transition_property_count() == 1 &&
1530                                      new_box_style.transition_combined_duration_at(0) <= 0.0f32;
1531         let new_display_style = new_box_style.clone_display();
1532         let old_display_style = old_values.get_box().clone_display();
1533 
1534         new_box_style.transition_property_count() > 0 &&
1535         !transition_not_running &&
1536         (new_display_style != Display::None &&
1537          old_display_style != Display::None)
1538     }
1539 
1540     // Detect if there are any changes that require us to update transitions.
1541     // This is used as a more thoroughgoing check than the, cheaper
1542     // might_need_transitions_update check.
1543     //
1544     // The following logic shadows the logic used on the Gecko side
1545     // (nsTransitionManager::DoUpdateTransitions) where we actually perform the
1546     // update.
1547     //
1548     // https://drafts.csswg.org/css-transitions/#starting
needs_transitions_update( &self, before_change_style: &ComputedValues, after_change_style: &ComputedValues, ) -> bool1549     fn needs_transitions_update(
1550         &self,
1551         before_change_style: &ComputedValues,
1552         after_change_style: &ComputedValues,
1553     ) -> bool {
1554         use gecko_bindings::structs::nsCSSPropertyID;
1555         use properties::LonghandIdSet;
1556 
1557         debug_assert!(self.might_need_transitions_update(Some(before_change_style),
1558                                                          after_change_style),
1559                       "We should only call needs_transitions_update if \
1560                        might_need_transitions_update returns true");
1561 
1562         let after_change_box_style = after_change_style.get_box();
1563         let transitions_count = after_change_box_style.transition_property_count();
1564         let existing_transitions = self.css_transitions_info();
1565 
1566         // Check if this property is none, custom or unknown.
1567         let is_none_or_custom_property = |property: nsCSSPropertyID| -> bool {
1568             return property == nsCSSPropertyID::eCSSPropertyExtra_no_properties ||
1569                    property == nsCSSPropertyID::eCSSPropertyExtra_variable ||
1570                    property == nsCSSPropertyID::eCSSProperty_UNKNOWN;
1571         };
1572 
1573         let mut transitions_to_keep = LonghandIdSet::new();
1574 
1575         for i in 0..transitions_count {
1576             let property = after_change_box_style.transition_nscsspropertyid_at(i);
1577             let combined_duration = after_change_box_style.transition_combined_duration_at(i);
1578 
1579             // We don't need to update transition for none/custom properties.
1580             if is_none_or_custom_property(property) {
1581                 continue;
1582             }
1583 
1584             let transition_property: TransitionProperty = property.into();
1585 
1586             let mut property_check_helper = |property: LonghandId| -> bool {
1587                 transitions_to_keep.insert(property);
1588                 self.needs_transitions_update_per_property(
1589                     property,
1590                     combined_duration,
1591                     before_change_style,
1592                     after_change_style,
1593                     &existing_transitions
1594                 )
1595             };
1596 
1597             match transition_property {
1598                 TransitionProperty::Unsupported(..) => {},
1599                 TransitionProperty::Shorthand(ref shorthand) => {
1600                     if shorthand.longhands().any(property_check_helper) {
1601                         return true;
1602                     }
1603                 },
1604                 TransitionProperty::Longhand(longhand_id) => {
1605                     if property_check_helper(longhand_id) {
1606                         return true;
1607                     }
1608                 },
1609             }
1610         }
1611 
1612         // Check if we have to cancel the running transition because this is not
1613         // a matching transition-property value.
1614         existing_transitions.keys().any(|property| {
1615             !transitions_to_keep.contains(*property)
1616         })
1617     }
1618 
1619     #[inline]
lang_attr(&self) -> Option<AttrValue>1620     fn lang_attr(&self) -> Option<AttrValue> {
1621         let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
1622         if ptr.is_null() {
1623             None
1624         } else {
1625             Some(unsafe { Atom::from_addrefed(ptr) })
1626         }
1627     }
1628 
match_element_lang( &self, override_lang: Option<Option<AttrValue>>, value: &PseudoClassStringArg ) -> bool1629     fn match_element_lang(
1630         &self,
1631         override_lang: Option<Option<AttrValue>>,
1632         value: &PseudoClassStringArg
1633     ) -> bool {
1634         // Gecko supports :lang() from CSS Selectors 3, which only accepts a
1635         // single language tag, and which performs simple dash-prefix matching
1636         // on it.
1637         debug_assert!(value.len() > 0 && value[value.len() - 1] == 0,
1638                       "expected value to be null terminated");
1639         let override_lang_ptr = match &override_lang {
1640             &Some(Some(ref atom)) => atom.as_ptr(),
1641             _ => ptr::null_mut(),
1642         };
1643         unsafe {
1644             Gecko_MatchLang(self.0, override_lang_ptr, override_lang.is_some(), value.as_ptr())
1645         }
1646     }
1647 
is_html_document_body_element(&self) -> bool1648     fn is_html_document_body_element(&self) -> bool {
1649         if self.local_name() != &*local_name!("body") {
1650             return false;
1651         }
1652 
1653         if !self.is_html_element() {
1654             return false;
1655         }
1656 
1657         unsafe { bindings::Gecko_IsDocumentBody(self.0) }
1658     }
1659 
synthesize_presentational_hints_for_legacy_attributes<V>( &self, visited_handling: VisitedHandlingMode, hints: &mut V ) where V: Push<ApplicableDeclarationBlock>,1660     fn synthesize_presentational_hints_for_legacy_attributes<V>(
1661         &self,
1662         visited_handling: VisitedHandlingMode,
1663         hints: &mut V
1664     )
1665     where
1666         V: Push<ApplicableDeclarationBlock>,
1667     {
1668         use properties::longhands::_x_lang::SpecifiedValue as SpecifiedLang;
1669         use properties::longhands::_x_text_zoom::SpecifiedValue as SpecifiedZoom;
1670         use properties::longhands::color::SpecifiedValue as SpecifiedColor;
1671         use properties::longhands::text_align::SpecifiedValue as SpecifiedTextAlign;
1672         use values::specified::color::Color;
1673         lazy_static! {
1674             static ref TH_RULE: ApplicableDeclarationBlock = {
1675                 let global_style_data = &*GLOBAL_STYLE_DATA;
1676                 let pdb = PropertyDeclarationBlock::with_one(
1677                     PropertyDeclaration::TextAlign(SpecifiedTextAlign::MozCenterOrInherit),
1678                     Importance::Normal
1679                 );
1680                 let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
1681                 ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
1682             };
1683             static ref TABLE_COLOR_RULE: ApplicableDeclarationBlock = {
1684                 let global_style_data = &*GLOBAL_STYLE_DATA;
1685                 let pdb = PropertyDeclarationBlock::with_one(
1686                     PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())),
1687                     Importance::Normal
1688                 );
1689                 let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
1690                 ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
1691             };
1692             static ref MATHML_LANG_RULE: ApplicableDeclarationBlock = {
1693                 let global_style_data = &*GLOBAL_STYLE_DATA;
1694                 let pdb = PropertyDeclarationBlock::with_one(
1695                     PropertyDeclaration::XLang(SpecifiedLang(atom!("x-math"))),
1696                     Importance::Normal
1697                 );
1698                 let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
1699                 ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
1700             };
1701             static ref SVG_TEXT_DISABLE_ZOOM_RULE: ApplicableDeclarationBlock = {
1702                 let global_style_data = &*GLOBAL_STYLE_DATA;
1703                 let pdb = PropertyDeclarationBlock::with_one(
1704                     PropertyDeclaration::XTextZoom(SpecifiedZoom(false)),
1705                     Importance::Normal
1706                 );
1707                 let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
1708                 ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
1709             };
1710         };
1711 
1712         let ns = self.namespace_id();
1713         // <th> elements get a default MozCenterOrInherit which may get overridden
1714         if ns == structs::kNameSpaceID_XHTML as i32 {
1715             if self.local_name().as_ptr() == atom!("th").as_ptr() {
1716                 hints.push(TH_RULE.clone());
1717             } else if self.local_name().as_ptr() == atom!("table").as_ptr() &&
1718                       self.as_node().owner_doc().quirks_mode() == QuirksMode::Quirks {
1719                 hints.push(TABLE_COLOR_RULE.clone());
1720             }
1721         }
1722         if ns == structs::kNameSpaceID_SVG as i32 {
1723             if self.local_name().as_ptr() == atom!("text").as_ptr() {
1724                 hints.push(SVG_TEXT_DISABLE_ZOOM_RULE.clone());
1725             }
1726         }
1727         let declarations = unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0) };
1728         let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
1729             declarations.and_then(|s| s.as_arc_opt());
1730         if let Some(decl) = declarations {
1731             hints.push(
1732                 ApplicableDeclarationBlock::from_declarations(decl.clone_arc(), ServoCascadeLevel::PresHints)
1733             );
1734         }
1735         let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0) };
1736         let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
1737             declarations.and_then(|s| s.as_arc_opt());
1738         if let Some(decl) = declarations {
1739             hints.push(
1740                 ApplicableDeclarationBlock::from_declarations(decl.clone_arc(), ServoCascadeLevel::PresHints)
1741             );
1742         }
1743 
1744         // Support for link, vlink, and alink presentation hints on <body>
1745         if self.is_link() {
1746             // Unvisited vs. visited styles are computed up-front based on the
1747             // visited mode (not the element's actual state).
1748             let declarations = match visited_handling {
1749                 VisitedHandlingMode::AllLinksVisitedAndUnvisited => {
1750                     unreachable!("We should never try to selector match with \
1751                                  AllLinksVisitedAndUnvisited");
1752                 },
1753                 VisitedHandlingMode::AllLinksUnvisited => unsafe {
1754                     Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0)
1755                 },
1756                 VisitedHandlingMode::RelevantLinkVisited => unsafe {
1757                     Gecko_GetVisitedLinkAttrDeclarationBlock(self.0)
1758                 },
1759             };
1760             let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
1761                 declarations.and_then(|s| s.as_arc_opt());
1762             if let Some(decl) = declarations {
1763                 hints.push(
1764                     ApplicableDeclarationBlock::from_declarations(decl.clone_arc(), ServoCascadeLevel::PresHints)
1765                 );
1766             }
1767 
1768             let active = self.state().intersects(NonTSPseudoClass::Active.state_flag());
1769             if active {
1770                 let declarations = unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0) };
1771                 let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
1772                     declarations.and_then(|s| s.as_arc_opt());
1773                 if let Some(decl) = declarations {
1774                     hints.push(
1775                         ApplicableDeclarationBlock::from_declarations(decl.clone_arc(), ServoCascadeLevel::PresHints)
1776                     );
1777                 }
1778             }
1779         }
1780 
1781         // xml:lang has precedence over lang, which can be
1782         // set by Gecko_GetHTMLPresentationAttrDeclarationBlock
1783         //
1784         // http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language
1785         let ptr = unsafe {
1786             bindings::Gecko_GetXMLLangValue(self.0)
1787         };
1788         if !ptr.is_null() {
1789             let global_style_data = &*GLOBAL_STYLE_DATA;
1790 
1791             let pdb = PropertyDeclarationBlock::with_one(
1792                 PropertyDeclaration::XLang(SpecifiedLang(unsafe { Atom::from_addrefed(ptr) })),
1793                 Importance::Normal
1794             );
1795             let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
1796             hints.push(ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints))
1797         }
1798         // MathML's default lang has precedence over both `lang` and `xml:lang`
1799         if ns == structs::kNameSpaceID_MathML as i32 {
1800             if self.local_name().as_ptr() == atom!("math").as_ptr() {
1801                 hints.push(MATHML_LANG_RULE.clone());
1802             }
1803         }
1804     }
1805 }
1806 
1807 impl<'le> PartialEq for GeckoElement<'le> {
1808     #[inline]
eq(&self, other: &Self) -> bool1809     fn eq(&self, other: &Self) -> bool {
1810         self.0 as *const _ == other.0 as *const _
1811     }
1812 }
1813 
1814 impl<'le> Eq for GeckoElement<'le> {}
1815 
1816 impl<'le> Hash for GeckoElement<'le> {
1817     #[inline]
hash<H: Hasher>(&self, state: &mut H)1818     fn hash<H: Hasher>(&self, state: &mut H) {
1819         (self.0 as *const RawGeckoElement).hash(state);
1820     }
1821 }
1822 
1823 impl<'le> ::selectors::Element for GeckoElement<'le> {
1824     type Impl = SelectorImpl;
1825 
1826     #[inline]
opaque(&self) -> OpaqueElement1827     fn opaque(&self) -> OpaqueElement {
1828         OpaqueElement::new(self.0)
1829     }
1830 
1831     #[inline]
parent_element(&self) -> Option<Self>1832     fn parent_element(&self) -> Option<Self> {
1833         // FIXME(emilio): This will need to jump across if the parent node is a
1834         // shadow root to get the shadow host.
1835         let parent_node = self.as_node().parent_node();
1836         parent_node.and_then(|n| n.as_element())
1837     }
1838 
1839     #[inline]
pseudo_element_originating_element(&self) -> Option<Self>1840     fn pseudo_element_originating_element(&self) -> Option<Self> {
1841         debug_assert!(self.implemented_pseudo_element().is_some());
1842         self.closest_non_native_anonymous_ancestor()
1843     }
1844 
1845     #[inline]
assigned_slot(&self) -> Option<Self>1846     fn assigned_slot(&self) -> Option<Self> {
1847         let slot = self.extended_slots()?._base.mAssignedSlot.mRawPtr;
1848 
1849         unsafe {
1850             Some(GeckoElement(&slot.as_ref()?._base._base._base._base))
1851         }
1852     }
1853 
1854     #[inline]
first_child_element(&self) -> Option<Self>1855     fn first_child_element(&self) -> Option<Self> {
1856         let mut child = self.as_node().first_child();
1857         while let Some(child_node) = child {
1858             if let Some(el) = child_node.as_element() {
1859                 return Some(el)
1860             }
1861             child = child_node.next_sibling();
1862         }
1863         None
1864     }
1865 
1866     #[inline]
last_child_element(&self) -> Option<Self>1867     fn last_child_element(&self) -> Option<Self> {
1868         let mut child = self.as_node().last_child();
1869         while let Some(child_node) = child {
1870             if let Some(el) = child_node.as_element() {
1871                 return Some(el)
1872             }
1873             child = child_node.prev_sibling();
1874         }
1875         None
1876     }
1877 
1878     #[inline]
prev_sibling_element(&self) -> Option<Self>1879     fn prev_sibling_element(&self) -> Option<Self> {
1880         let mut sibling = self.as_node().prev_sibling();
1881         while let Some(sibling_node) = sibling {
1882             if let Some(el) = sibling_node.as_element() {
1883                 return Some(el)
1884             }
1885             sibling = sibling_node.prev_sibling();
1886         }
1887         None
1888     }
1889 
1890     #[inline]
next_sibling_element(&self) -> Option<Self>1891     fn next_sibling_element(&self) -> Option<Self> {
1892         let mut sibling = self.as_node().next_sibling();
1893         while let Some(sibling_node) = sibling {
1894             if let Some(el) = sibling_node.as_element() {
1895                 return Some(el)
1896             }
1897             sibling = sibling_node.next_sibling();
1898         }
1899         None
1900     }
1901 
attr_matches( &self, ns: &NamespaceConstraint<&Namespace>, local_name: &Atom, operation: &AttrSelectorOperation<&Atom> ) -> bool1902     fn attr_matches(
1903         &self,
1904         ns: &NamespaceConstraint<&Namespace>,
1905         local_name: &Atom,
1906         operation: &AttrSelectorOperation<&Atom>
1907     ) -> bool {
1908         unsafe {
1909             match *operation {
1910                 AttrSelectorOperation::Exists => {
1911                     bindings::Gecko_HasAttr(self.0,
1912                                             ns.atom_or_null(),
1913                                             local_name.as_ptr())
1914                 }
1915                 AttrSelectorOperation::WithValue { operator, case_sensitivity, expected_value } => {
1916                     let ignore_case = match case_sensitivity {
1917                         CaseSensitivity::CaseSensitive => false,
1918                         CaseSensitivity::AsciiCaseInsensitive => true,
1919                     };
1920                     // FIXME: case sensitivity for operators other than Equal
1921                     match operator {
1922                         AttrSelectorOperator::Equal => bindings::Gecko_AttrEquals(
1923                             self.0,
1924                             ns.atom_or_null(),
1925                             local_name.as_ptr(),
1926                             expected_value.as_ptr(),
1927                             ignore_case
1928                         ),
1929                         AttrSelectorOperator::Includes => bindings::Gecko_AttrIncludes(
1930                             self.0,
1931                             ns.atom_or_null(),
1932                             local_name.as_ptr(),
1933                             expected_value.as_ptr(),
1934                             ignore_case,
1935                         ),
1936                         AttrSelectorOperator::DashMatch => bindings::Gecko_AttrDashEquals(
1937                             self.0,
1938                             ns.atom_or_null(),
1939                             local_name.as_ptr(),
1940                             expected_value.as_ptr(),
1941                             ignore_case,
1942                         ),
1943                         AttrSelectorOperator::Prefix => bindings::Gecko_AttrHasPrefix(
1944                             self.0,
1945                             ns.atom_or_null(),
1946                             local_name.as_ptr(),
1947                             expected_value.as_ptr(),
1948                             ignore_case,
1949                         ),
1950                         AttrSelectorOperator::Suffix => bindings::Gecko_AttrHasSuffix(
1951                             self.0,
1952                             ns.atom_or_null(),
1953                             local_name.as_ptr(),
1954                             expected_value.as_ptr(),
1955                             ignore_case,
1956                         ),
1957                         AttrSelectorOperator::Substring => bindings::Gecko_AttrHasSubstring(
1958                             self.0,
1959                             ns.atom_or_null(),
1960                             local_name.as_ptr(),
1961                             expected_value.as_ptr(),
1962                             ignore_case,
1963                         ),
1964                     }
1965                 }
1966             }
1967         }
1968     }
1969 
1970     #[inline]
is_root(&self) -> bool1971     fn is_root(&self) -> bool {
1972         let parent_node = match self.as_node().parent_node() {
1973             Some(parent_node) => parent_node,
1974             None => return false,
1975         };
1976 
1977         if !parent_node.is_document() {
1978             return false;
1979         }
1980 
1981         unsafe {
1982             Gecko_IsRootElement(self.0)
1983         }
1984     }
1985 
is_empty(&self) -> bool1986     fn is_empty(&self) -> bool {
1987         !self.as_node().dom_children().any(|child| unsafe {
1988             Gecko_IsSignificantChild(child.0, true)
1989         })
1990     }
1991 
1992     #[inline]
local_name(&self) -> &WeakAtom1993     fn local_name(&self) -> &WeakAtom {
1994         unsafe {
1995             WeakAtom::new(self.as_node().node_info().mInner.mName)
1996         }
1997     }
1998 
1999     #[inline]
namespace(&self) -> &WeakNamespace2000     fn namespace(&self) -> &WeakNamespace {
2001         unsafe {
2002             let namespace_manager = structs::nsContentUtils_sNameSpaceManager;
2003             WeakNamespace::new((*namespace_manager).mURIArray[self.namespace_id() as usize].mRawPtr)
2004         }
2005     }
2006 
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),2007     fn match_non_ts_pseudo_class<F>(
2008         &self,
2009         pseudo_class: &NonTSPseudoClass,
2010         context: &mut MatchingContext<Self::Impl>,
2011         flags_setter: &mut F,
2012     ) -> bool
2013     where
2014         F: FnMut(&Self, ElementSelectorFlags),
2015     {
2016         use selectors::matching::*;
2017         match *pseudo_class {
2018             NonTSPseudoClass::Focus |
2019             NonTSPseudoClass::Enabled |
2020             NonTSPseudoClass::Disabled |
2021             NonTSPseudoClass::Checked |
2022             NonTSPseudoClass::Fullscreen |
2023             NonTSPseudoClass::MozFullScreen |
2024             NonTSPseudoClass::Indeterminate |
2025             NonTSPseudoClass::PlaceholderShown |
2026             NonTSPseudoClass::Target |
2027             NonTSPseudoClass::Valid |
2028             NonTSPseudoClass::Invalid |
2029             NonTSPseudoClass::MozUIValid |
2030             NonTSPseudoClass::MozBroken |
2031             NonTSPseudoClass::MozUserDisabled |
2032             NonTSPseudoClass::MozSuppressed |
2033             NonTSPseudoClass::MozLoading |
2034             NonTSPseudoClass::MozHandlerBlocked |
2035             NonTSPseudoClass::MozHandlerDisabled |
2036             NonTSPseudoClass::MozHandlerCrashed |
2037             NonTSPseudoClass::Required |
2038             NonTSPseudoClass::Optional |
2039             NonTSPseudoClass::MozReadOnly |
2040             NonTSPseudoClass::MozReadWrite |
2041             NonTSPseudoClass::FocusWithin |
2042             NonTSPseudoClass::MozDragOver |
2043             NonTSPseudoClass::MozDevtoolsHighlighted |
2044             NonTSPseudoClass::MozStyleeditorTransitioning |
2045             NonTSPseudoClass::MozFocusRing |
2046             NonTSPseudoClass::MozHandlerClickToPlay |
2047             NonTSPseudoClass::MozHandlerVulnerableUpdatable |
2048             NonTSPseudoClass::MozHandlerVulnerableNoUpdate |
2049             NonTSPseudoClass::MozMathIncrementScriptLevel |
2050             NonTSPseudoClass::InRange |
2051             NonTSPseudoClass::OutOfRange |
2052             NonTSPseudoClass::Default |
2053             NonTSPseudoClass::MozSubmitInvalid |
2054             NonTSPseudoClass::MozUIInvalid |
2055             NonTSPseudoClass::MozMeterOptimum |
2056             NonTSPseudoClass::MozMeterSubOptimum |
2057             NonTSPseudoClass::MozMeterSubSubOptimum |
2058             NonTSPseudoClass::MozHasDirAttr |
2059             NonTSPseudoClass::MozDirAttrLTR |
2060             NonTSPseudoClass::MozDirAttrRTL |
2061             NonTSPseudoClass::MozDirAttrLikeAuto |
2062             NonTSPseudoClass::MozAutofill |
2063             NonTSPseudoClass::Active |
2064             NonTSPseudoClass::Hover |
2065             NonTSPseudoClass::MozAutofillPreview => {
2066                 self.state().intersects(pseudo_class.state_flag())
2067             },
2068             NonTSPseudoClass::AnyLink => self.is_link(),
2069             NonTSPseudoClass::Link => {
2070                 self.is_link() && context.visited_handling().matches_unvisited()
2071             }
2072             NonTSPseudoClass::Visited => {
2073                 self.is_link() && context.visited_handling().matches_visited()
2074             }
2075             NonTSPseudoClass::MozFirstNode => {
2076                 flags_setter(self, ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
2077                 let mut elem = self.as_node();
2078                 while let Some(prev) = elem.prev_sibling() {
2079                     if prev.contains_non_whitespace_content() {
2080                         return false
2081                     }
2082                     elem = prev;
2083                 }
2084                 true
2085             }
2086             NonTSPseudoClass::MozLastNode => {
2087                 flags_setter(self, ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);
2088                 let mut elem = self.as_node();
2089                 while let Some(next) = elem.next_sibling() {
2090                     if next.contains_non_whitespace_content() {
2091                         return false
2092                     }
2093                     elem = next;
2094                 }
2095                 true
2096             }
2097             NonTSPseudoClass::MozOnlyWhitespace => {
2098                 flags_setter(self, ElementSelectorFlags::HAS_EMPTY_SELECTOR);
2099                 if self.as_node().dom_children().any(|c| c.contains_non_whitespace_content()) {
2100                     return false
2101                 }
2102                 true
2103             }
2104             NonTSPseudoClass::MozTableBorderNonzero |
2105             NonTSPseudoClass::MozBrowserFrame |
2106             NonTSPseudoClass::MozNativeAnonymous |
2107             NonTSPseudoClass::MozUseShadowTreeRoot => unsafe {
2108                 Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
2109             },
2110             NonTSPseudoClass::MozIsHTML => {
2111                 self.is_html_element_in_html_document()
2112             }
2113             NonTSPseudoClass::MozLWTheme => {
2114                 self.document_theme() != DocumentTheme::Doc_Theme_None
2115             }
2116             NonTSPseudoClass::MozLWThemeBrightText => {
2117                 self.document_theme() == DocumentTheme::Doc_Theme_Bright
2118             }
2119             NonTSPseudoClass::MozLWThemeDarkText => {
2120                 self.document_theme() == DocumentTheme::Doc_Theme_Dark
2121             }
2122             NonTSPseudoClass::MozWindowInactive => {
2123                 let state_bit = DocumentState::NS_DOCUMENT_STATE_WINDOW_INACTIVE;
2124                 if context.extra_data.document_state.intersects(state_bit) {
2125                     return !context.in_negation();
2126                 }
2127 
2128                 self.document_state().contains(state_bit)
2129             }
2130             NonTSPseudoClass::MozPlaceholder => false,
2131             NonTSPseudoClass::MozAny(ref sels) => {
2132                 context.nest(|context| {
2133                     sels.iter().any(|s| {
2134                         matches_complex_selector(s.iter(), self, context, flags_setter)
2135                     })
2136                 })
2137             }
2138             NonTSPseudoClass::Lang(ref lang_arg) => {
2139                 self.match_element_lang(None, lang_arg)
2140             }
2141             NonTSPseudoClass::MozLocaleDir(ref dir) => {
2142                 let state_bit = DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE;
2143                 if context.extra_data.document_state.intersects(state_bit) {
2144                     // NOTE(emilio): We could still return false for
2145                     // Direction::Other(..), but we don't bother.
2146                     return !context.in_negation();
2147                 }
2148 
2149                 let doc_is_rtl = self.document_state().contains(state_bit);
2150 
2151                 match **dir {
2152                     Direction::Ltr => !doc_is_rtl,
2153                     Direction::Rtl => doc_is_rtl,
2154                     Direction::Other(..) => false,
2155                 }
2156             }
2157             NonTSPseudoClass::Dir(ref dir) => {
2158                 match **dir {
2159                     Direction::Ltr => self.state().intersects(ElementState::IN_LTR_STATE),
2160                     Direction::Rtl => self.state().intersects(ElementState::IN_RTL_STATE),
2161                     Direction::Other(..) => false,
2162                 }
2163             }
2164         }
2165     }
2166 
match_pseudo_element( &self, pseudo_element: &PseudoElement, _context: &mut MatchingContext<Self::Impl>, ) -> bool2167     fn match_pseudo_element(
2168         &self,
2169         pseudo_element: &PseudoElement,
2170         _context: &mut MatchingContext<Self::Impl>,
2171     ) -> bool {
2172         // TODO(emilio): I believe we could assert we are a pseudo-element and
2173         // match the proper pseudo-element, given how we rulehash the stuff
2174         // based on the pseudo.
2175         match self.implemented_pseudo_element() {
2176             Some(ref pseudo) => *pseudo == pseudo_element.canonical(),
2177             None => false,
2178         }
2179     }
2180 
2181     #[inline]
is_link(&self) -> bool2182     fn is_link(&self) -> bool {
2183         self.state().intersects(NonTSPseudoClass::AnyLink.state_flag())
2184     }
2185 
2186     #[inline]
has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool2187     fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2188         if !self.has_id() {
2189             return false
2190         }
2191 
2192         unsafe {
2193             let ptr = bindings::Gecko_AtomAttrValue(self.0, atom!("id").as_ptr());
2194 
2195             if ptr.is_null() {
2196                 false
2197             } else {
2198                 case_sensitivity.eq_atom(WeakAtom::new(ptr), id)
2199             }
2200         }
2201     }
2202 
2203     #[inline(always)]
has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool2204     fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2205         if !self.may_have_class() {
2206             return false;
2207         }
2208 
2209         snapshot_helpers::has_class(
2210             self.0,
2211             name,
2212             case_sensitivity,
2213             bindings::Gecko_HasClass,
2214         )
2215     }
2216 
2217     #[inline]
is_html_element_in_html_document(&self) -> bool2218     fn is_html_element_in_html_document(&self) -> bool {
2219         self.is_html_element() &&
2220         self.as_node().owner_doc().is_html_document()
2221     }
2222 
2223     #[inline]
is_html_slot_element(&self) -> bool2224     fn is_html_slot_element(&self) -> bool {
2225         self.is_html_element() &&
2226         self.local_name().as_ptr() == local_name!("slot").as_ptr()
2227     }
2228 
2229     #[inline]
ignores_nth_child_selectors(&self) -> bool2230     fn ignores_nth_child_selectors(&self) -> bool {
2231         self.is_root_of_anonymous_subtree()
2232     }
2233 
2234     #[inline]
blocks_ancestor_combinators(&self) -> bool2235     fn blocks_ancestor_combinators(&self) -> bool {
2236         if !self.is_root_of_anonymous_subtree() {
2237             return false
2238         }
2239 
2240         match self.parent_element() {
2241             Some(e) => {
2242                 // If this element is the shadow root of an use-element shadow
2243                 // tree, according to the spec, we should not match rules
2244                 // cross the shadow DOM boundary.
2245                 e.local_name() == &*local_name!("use") &&
2246                 e.namespace() == &*ns!("http://www.w3.org/2000/svg")
2247             },
2248             None => false,
2249         }
2250     }
2251 }
2252 
2253 /// A few helpers to help with attribute selectors and snapshotting.
2254 pub trait NamespaceConstraintHelpers {
2255     /// Returns the namespace of the selector, or null otherwise.
atom_or_null(&self) -> *mut nsAtom2256     fn atom_or_null(&self) -> *mut nsAtom;
2257 }
2258 
2259 impl<'a> NamespaceConstraintHelpers for NamespaceConstraint<&'a Namespace> {
atom_or_null(&self) -> *mut nsAtom2260     fn atom_or_null(&self) -> *mut nsAtom {
2261         match *self {
2262             NamespaceConstraint::Any => ptr::null_mut(),
2263             NamespaceConstraint::Specific(ref ns) => ns.0.as_ptr(),
2264         }
2265     }
2266 }
2267