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