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