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