From ec30a6eea6853929a75eda75ba1b6afcaf0f335d Mon Sep 17 00:00:00 2001 From: David Shin Date: Tue, 1 Aug 2023 00:29:56 +0000 Subject: [PATCH] Bug 1845744: Move selector-specific node flags to a separate field. r=emilio Upcoming :has invalidation (Bug 1792501) requires 4 more flags, and we're out of space. This change consumes the remaining 32-bit hole in `nsINode` to migrate selector-specific node flags. This has implications on 32-bit platforms, specifically on text nodes. Differential Revision: https://phabricator.services.mozilla.com/D184718 --- dom/base/Element.cpp | 4 +- dom/base/nsINode.h | 104 +++++++++++++----------- dom/base/nsWrapperCache.h | 5 +- layout/base/RestyleManager.cpp | 82 ++++++++++--------- layout/base/RestyleManager.h | 3 +- layout/style/ServoBindings.toml | 2 + servo/components/style/gecko/wrapper.rs | 46 ++++++----- 7 files changed, 139 insertions(+), 107 deletions(-) diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index f6dd7501139f..cdc5be8ef209 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -231,7 +231,7 @@ ASSERT_NODE_SIZE(HTMLParagraphElement, 128, 80); ASSERT_NODE_SIZE(HTMLPreElement, 128, 80); ASSERT_NODE_SIZE(HTMLSpanElement, 128, 80); ASSERT_NODE_SIZE(HTMLTableCellElement, 128, 80); -ASSERT_NODE_SIZE(Text, 120, 64); +ASSERT_NODE_SIZE(Text, 120, 80); #undef ASSERT_NODE_SIZE #undef EXTRA_DOM_NODE_BYTES @@ -3025,6 +3025,8 @@ void Element::List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const { fprintf(out, " state=[%llx]", static_cast(State().GetInternalValue())); fprintf(out, " flags=[%08x]", static_cast(GetFlags())); + fprintf(out, " selectorflags=[%08x]", + static_cast(GetSelectorFlags())); if (IsClosestCommonInclusiveAncestorForRangeInSelection()) { const LinkedList* ranges = GetExistingClosestCommonInclusiveAncestorRanges(); diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index 3617a4a3e315..9554650de24d 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -143,74 +143,82 @@ enum : uint32_t { // Whether the node participates in a shadow tree. NODE_IS_IN_SHADOW_TREE = NODE_FLAG_BIT(5), - // Node has an :empty or :-moz-only-whitespace selector - NODE_HAS_EMPTY_SELECTOR = NODE_FLAG_BIT(6), - - // A child of the node has a selector such that any insertion, - // removal, or appending of children requires restyling the parent, if the - // parent is an element. If the parent is the shadow root, the child's - // siblings are restyled. - NODE_HAS_SLOW_SELECTOR = NODE_FLAG_BIT(7), - - // A child of the node has a :first-child, :-moz-first-node, - // :only-child, :last-child or :-moz-last-node selector. - NODE_HAS_EDGE_CHILD_SELECTOR = NODE_FLAG_BIT(8), - - // A child of the node has a selector such that any insertion or - // removal of children requires restyling later siblings of that - // element. Additionally (in this manner it is stronger than - // NODE_HAS_SLOW_SELECTOR), if a child's style changes due to any - // other content tree changes (e.g., the child changes to or from - // matching :empty due to a grandchild insertion or removal), the - // child's later siblings must also be restyled. - NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS = NODE_FLAG_BIT(9), - - // A child of this node might be matched by :nth-child(.. of ) or - // :nth-last-child(.. of ). If a DOM mutation may have caused the - // selector to either match or no longer match that child, the child's - // siblings are restyled. - NODE_HAS_SLOW_SELECTOR_NTH_OF = NODE_FLAG_BIT(10), - - // Set of selector flags that may trigger a restyle as a result of DOM - // mutation. - NODE_RESTYLE_SELECTOR_FLAGS = - NODE_HAS_EMPTY_SELECTOR | NODE_HAS_SLOW_SELECTOR | - NODE_HAS_EDGE_CHILD_SELECTOR | NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS | - NODE_HAS_SLOW_SELECTOR_NTH_OF, - // This node needs to go through frame construction to get a frame (or // undisplayed entry). - NODE_NEEDS_FRAME = NODE_FLAG_BIT(11), + NODE_NEEDS_FRAME = NODE_FLAG_BIT(6), // At least one descendant in the flattened tree has NODE_NEEDS_FRAME set. // This should be set on every node on the flattened tree path between the // node(s) with NODE_NEEDS_FRAME and the root content. - NODE_DESCENDANTS_NEED_FRAMES = NODE_FLAG_BIT(12), + NODE_DESCENDANTS_NEED_FRAMES = NODE_FLAG_BIT(7), // Set if the node has the accesskey attribute set. - NODE_HAS_ACCESSKEY = NODE_FLAG_BIT(13), + NODE_HAS_ACCESSKEY = NODE_FLAG_BIT(8), // Set if the node has right-to-left directionality - NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(14), + NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(9), // Set if the node has left-to-right directionality - NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(15), + NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(10), NODE_ALL_DIRECTION_FLAGS = NODE_HAS_DIRECTION_LTR | NODE_HAS_DIRECTION_RTL, - NODE_HAS_BEEN_IN_UA_WIDGET = NODE_FLAG_BIT(16), + NODE_HAS_BEEN_IN_UA_WIDGET = NODE_FLAG_BIT(11), // Set if the node has a nonce value and a header delivered CSP. - NODE_HAS_NONCE_AND_HEADER_CSP = NODE_FLAG_BIT(17), + NODE_HAS_NONCE_AND_HEADER_CSP = NODE_FLAG_BIT(12), - NODE_KEEPS_DOMARENA = NODE_FLAG_BIT(18), + NODE_KEEPS_DOMARENA = NODE_FLAG_BIT(13), - NODE_MAY_HAVE_ELEMENT_CHILDREN = NODE_FLAG_BIT(19), + NODE_MAY_HAVE_ELEMENT_CHILDREN = NODE_FLAG_BIT(14), // Remaining bits are node type specific. - NODE_TYPE_SPECIFIC_BITS_OFFSET = 20 + NODE_TYPE_SPECIFIC_BITS_OFFSET = 15 }; +// Flags for selectors that persist to the DOM node. +enum class NodeSelectorFlags : uint32_t { + // Node has an :empty or :-moz-only-whitespace selector + HasEmptySelector = 1 << 0, + + /// A child of the node has a selector such that any insertion, + /// removal, or appending of children requires restyling the parent, if the + /// parent is an element. If the parent is the shadow root, the child's + /// siblings are restyled. + HasSlowSelector = 1 << 1, + + /// A child of the node has a :first-child, :-moz-first-node, + /// :only-child, :last-child or :-moz-last-node selector. + HasEdgeChildSelector = 1 << 2, + + /// A child of the node has a selector such that any insertion or + /// removal of children requires restyling later siblings of that + /// element. Additionally (in this manner it is stronger than + /// NODE_HAS_SLOW_SELECTOR), if a child's style changes due to any + /// other content tree changes (e.g., the child changes to or from + /// matching :empty due to a grandchild insertion or removal), the + /// child's later siblings must also be restyled. + HasSlowSelectorLaterSiblings = 1 << 3, + + /// A child of this node might be matched by :nth-child(.. of ) or + /// :nth-last-child(.. of ). If a DOM mutation may have caused the + /// selector to either match or no longer match that child, the child's + /// siblings are restyled. + HasSlowSelectorNthOf = 1 << 4, + + /// Set of selector flags that may trigger a restyle on DOM append, with + /// restyle on siblings or a single parent (And perhaps their subtrees). + AllSimpleRestyleFlagsForAppend = HasEmptySelector | HasSlowSelector | + HasEdgeChildSelector | HasSlowSelectorNthOf, + + /// Set of selector flags that may trigger a restyle as a result of any + /// DOM mutation. + AllSimpleRestyleFlags = + AllSimpleRestyleFlagsForAppend | HasSlowSelectorLaterSiblings, +}; + +MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(NodeSelectorFlags); + // Make sure we have space for our bits #define ASSERT_NODE_FLAGS_SPACE(n) \ static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <= \ @@ -2317,6 +2325,10 @@ class nsINode : public mozilla::dom::EventTarget { #undef TOUCH_EVENT #undef EVENT + NodeSelectorFlags GetSelectorFlags() const { + return static_cast(mSelectorFlags.Get()); + } + protected: static bool Traverse(nsINode* tmp, nsCycleCollectionTraversalCallback& cb); static void Unlink(nsINode* tmp); @@ -2334,7 +2346,7 @@ class nsINode : public mozilla::dom::EventTarget { uint32_t mBoolFlags; #endif - // NOTE, there are 32 bits left here, at least in 64 bit builds. + mozilla::RustCell mSelectorFlags{0}; uint32_t mChildCount; diff --git a/dom/base/nsWrapperCache.h b/dom/base/nsWrapperCache.h index d7232470d958..f1c4e17fa31c 100644 --- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -40,7 +40,10 @@ class nsWindowRoot; // Both sets of flags are 32 bits. On 64-bit platforms, this can cause two // wasted 32-bit fields due to alignment requirements. Some compilers are // smart enough to coalesce the fields if we make mBoolFlags the first member -// of nsINode, but others (such as MSVC) are not. +// of nsINode, but others (such as MSVC, but that's not officially supported +// by us anymore) are not. Also note that this kind of coalascing tends to +// interact poorly with rust's bindgen, see: +// https://github.com/rust-lang/rust-bindgen/issues/380 // // So we just store mBoolFlags directly on nsWrapperCache on 64-bit platforms. // This may waste space for some other nsWrapperCache-derived objects that have diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index ea02c1af89dd..15e697f2416a 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -94,17 +94,16 @@ void RestyleManager::ContentAppended(nsIContent* aFirstNewContent) { } } #endif - uint32_t selectorFlags = - container->GetFlags() & - (NODE_RESTYLE_SELECTOR_FLAGS & ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS); - if (selectorFlags == 0) { + const auto selectorFlags = container->GetSelectorFlags() & + NodeSelectorFlags::AllSimpleRestyleFlagsForAppend; + if (!selectorFlags) { return; } // The container cannot be a document. MOZ_ASSERT(container->IsElement() || container->IsShadowRoot()); - if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) { + if (selectorFlags & NodeSelectorFlags::HasEmptySelector) { // see whether we need to restyle the container bool wasEmpty = true; // :empty or :-moz-only-whitespace for (nsIContent* cur = container->GetFirstChild(); cur != aFirstNewContent; @@ -124,7 +123,7 @@ void RestyleManager::ContentAppended(nsIContent* aFirstNewContent) { } } - if (selectorFlags & NODE_HAS_SLOW_SELECTOR) { + if (selectorFlags & NodeSelectorFlags::HasSlowSelector) { if (container->IsElement()) { PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(), nsChangeHint(0)); @@ -136,7 +135,7 @@ void RestyleManager::ContentAppended(nsIContent* aFirstNewContent) { return; } - if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) { + if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) { // restyle the last element child before this node for (nsIContent* cur = aFirstNewContent->GetPreviousSibling(); cur; cur = cur->GetPreviousSibling()) { @@ -173,8 +172,8 @@ void RestyleManager::RestyleForEmptyChange(Element* aContainer) { // In some cases (:empty + E, :empty ~ E), a change in the content of // an element requires restyling its parent's siblings. nsIContent* grandparent = aContainer->GetParent(); - if (!grandparent || - !(grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) { + if (!grandparent || !(grandparent->GetSelectorFlags() & + NodeSelectorFlags::HasSlowSelectorLaterSiblings)) { return; } RestyleSiblingsStartingWith(aContainer->GetNextSibling()); @@ -182,7 +181,8 @@ void RestyleManager::RestyleForEmptyChange(Element* aContainer) { void RestyleManager::MaybeRestyleForEdgeChildChange(nsINode* aContainer, nsIContent* aChangedChild) { - MOZ_ASSERT(aContainer->GetFlags() & NODE_HAS_EDGE_CHILD_SELECTOR); + MOZ_ASSERT(aContainer->GetSelectorFlags() & + NodeSelectorFlags::HasEdgeChildSelector); MOZ_ASSERT(aChangedChild->GetParent() == aContainer); // restyle the previously-first element child if it is after this node bool passedChild = false; @@ -264,9 +264,10 @@ void RestyleManager::CharacterDataChanged( nsINode* parent = aContent->GetParentNode(); MOZ_ASSERT(parent, "How were we notified of a stray node?"); - uint32_t slowSelectorFlags = parent->GetFlags() & NODE_RESTYLE_SELECTOR_FLAGS; - if (!(slowSelectorFlags & - (NODE_HAS_EMPTY_SELECTOR | NODE_HAS_EDGE_CHILD_SELECTOR))) { + const auto slowSelectorFlags = + parent->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags; + if (!(slowSelectorFlags & (NodeSelectorFlags::HasEmptySelector | + NodeSelectorFlags::HasEdgeChildSelector))) { // Nothing to do, no other slow selector can change as a result of this. return; } @@ -321,7 +322,7 @@ void RestyleManager::CharacterDataChanged( return; } - if (slowSelectorFlags & NODE_HAS_EMPTY_SELECTOR) { + if (slowSelectorFlags & NodeSelectorFlags::HasEmptySelector) { if (!HasAnySignificantSibling(parent->AsElement(), aContent)) { // We used to be empty, restyle the parent. RestyleForEmptyChange(parent->AsElement()); @@ -329,7 +330,7 @@ void RestyleManager::CharacterDataChanged( } } - if (slowSelectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) { + if (slowSelectorFlags & NodeSelectorFlags::HasEdgeChildSelector) { MaybeRestyleForEdgeChildChange(parent, aContent); } } @@ -344,8 +345,9 @@ void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) { nsINode* container = aChild->GetParentNode(); MOZ_ASSERT(container); - uint32_t selectorFlags = container->GetFlags() & NODE_RESTYLE_SELECTOR_FLAGS; - if (selectorFlags == 0) { + const auto selectorFlags = + container->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags; + if (!selectorFlags) { return; } @@ -355,7 +357,8 @@ void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) { // The container cannot be a document. MOZ_ASSERT(container->IsElement() || container->IsShadowRoot()); - if (selectorFlags & NODE_HAS_EMPTY_SELECTOR && container->IsElement()) { + if (selectorFlags & NodeSelectorFlags::HasEmptySelector && + container->IsElement()) { // See whether we need to restyle the container due to :empty / // :-moz-only-whitespace. const bool wasEmpty = @@ -369,7 +372,7 @@ void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) { } } - if (selectorFlags & NODE_HAS_SLOW_SELECTOR) { + if (selectorFlags & NodeSelectorFlags::HasSlowSelector) { if (container->IsElement()) { PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(), nsChangeHint(0)); @@ -381,12 +384,12 @@ void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) { return; } - if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) { + if (selectorFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings) { // Restyle all later siblings. RestyleSiblingsStartingWith(aChild->GetNextSibling()); } - if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) { + if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) { MaybeRestyleForEdgeChildChange(container, aChild); } } @@ -408,8 +411,9 @@ void RestyleManager::ContentRemoved(nsIContent* aOldChild, IncrementUndisplayedRestyleGeneration(); } - uint32_t selectorFlags = container->GetFlags() & NODE_RESTYLE_SELECTOR_FLAGS; - if (selectorFlags == 0) { + const auto selectorFlags = + container->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags; + if (!selectorFlags) { return; } @@ -424,7 +428,8 @@ void RestyleManager::ContentRemoved(nsIContent* aOldChild, // The container cannot be a document. MOZ_ASSERT(container->IsElement() || container->IsShadowRoot()); - if (selectorFlags & NODE_HAS_EMPTY_SELECTOR && container->IsElement()) { + if (selectorFlags & NodeSelectorFlags::HasEmptySelector && + container->IsElement()) { // see whether we need to restyle the container bool isEmpty = true; // :empty or :-moz-only-whitespace for (nsIContent* child = container->GetFirstChild(); child; @@ -444,7 +449,7 @@ void RestyleManager::ContentRemoved(nsIContent* aOldChild, } } - if (selectorFlags & NODE_HAS_SLOW_SELECTOR) { + if (selectorFlags & NodeSelectorFlags::HasSlowSelector) { if (container->IsElement()) { PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(), nsChangeHint(0)); @@ -456,12 +461,12 @@ void RestyleManager::ContentRemoved(nsIContent* aOldChild, return; } - if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) { + if (selectorFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings) { // Restyle all later siblings. RestyleSiblingsStartingWith(aFollowingSibling); } - if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) { + if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) { // restyle the now-first element child if it was after aOldChild bool reachedFollowingSibling = false; for (nsIContent* content = container->GetFirstChild(); content; @@ -3355,8 +3360,8 @@ void RestyleManager::MaybeRestyleForNthOfState(ServoStyleSet& aStyleSet, ElementState aChangedBits) { const auto* parentNode = aChild->GetParentNode(); MOZ_ASSERT(parentNode); - const auto parentFlags = parentNode->GetFlags(); - if (!(parentFlags & NODE_HAS_SLOW_SELECTOR_NTH_OF)) { + const auto parentFlags = parentNode->GetSelectorFlags(); + if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) { return; } @@ -3535,20 +3540,21 @@ void RestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID, } } -void RestyleManager::RestyleSiblings( - Element* aChild, nsBaseContentList::FlagsType aParentFlags) { +void RestyleManager::RestyleSiblings(Element* aChild, + NodeSelectorFlags aParentFlags) { const DebugOnly parentNode = aChild->GetParentNode(); MOZ_ASSERT(parentNode->IsElement() || parentNode->IsShadowRoot()); DebugOnly restyledSiblings = false; - // NODE_HAS_SLOW_SELECTOR typically indicates restyling the parent, but since - // we know we're restyling for :nth-last-child(.. of ), we can - // restyle only previous siblings without under-invalidating. - if (aParentFlags & NODE_HAS_SLOW_SELECTOR) { + // NodeSelectorFlags::HasSlowSelector typically indicates restyling the + // parent, but since we know we're restyling for :nth-last-child(.. of + // ), we can restyle only previous siblings without + // under-invalidating. + if (aParentFlags & NodeSelectorFlags::HasSlowSelector) { RestylePreviousSiblings(aChild->GetPreviousSibling()); restyledSiblings = true; } - if (aParentFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) { + if (aParentFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings) { RestyleSiblingsStartingWith(aChild->GetNextSibling()); restyledSiblings = true; } @@ -3560,8 +3566,8 @@ void RestyleManager::MaybeRestyleForNthOfAttribute( Element* aChild, nsAtom* aAttribute, const nsAttrValue* aOldValue) { const auto* parentNode = aChild->GetParentNode(); MOZ_ASSERT(parentNode); - const auto parentFlags = parentNode->GetFlags(); - if (!(parentFlags & NODE_HAS_SLOW_SELECTOR_NTH_OF)) { + const auto parentFlags = parentNode->GetSelectorFlags(); + if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) { return; } if (!aChild->HasServoData()) { diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h index 773bbfc05902..bd18947f01dd 100644 --- a/layout/base/RestyleManager.h +++ b/layout/base/RestyleManager.h @@ -379,8 +379,7 @@ class RestyleManager { /** * Restyle an element's previous and/or next siblings. */ - void RestyleSiblings(dom::Element* aChild, - nsBaseContentList::FlagsType aParentFlags); + void RestyleSiblings(dom::Element* aChild, NodeSelectorFlags aParentFlags); /** * Posts restyle hints for siblings of an element and their descendants if the diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index c73b90a3de39..59d55792bcc4 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -58,6 +58,7 @@ hide-types = [ bitfield-enums = [ "nsChangeHint", "mozilla::OriginFlags", + "NodeSelectorFlags", ] rusty-enums = [ "nsCompatibility", @@ -354,6 +355,7 @@ allowlist-types = [ "mozilla::dom::MediaList", "mozilla::StyleRuleInclusion", "nsStyleTransformMatrix::MatrixTransformOperator", + "NodeSelectorFlags", ] opaque-types = [ "mozilla::StyleThinArc", # https://github.com/rust-lang/rust-bindgen/issues/1557 diff --git a/servo/components/style/gecko/wrapper.rs b/servo/components/style/gecko/wrapper.rs index d61f762b2214..39a0cc3163f9 100644 --- a/servo/components/style/gecko/wrapper.rs +++ b/servo/components/style/gecko/wrapper.rs @@ -76,6 +76,7 @@ use selectors::matching::{ElementSelectorFlags, MatchingContext}; use selectors::sink::Push; use selectors::{Element, OpaqueElement}; use servo_arc::{Arc, ArcBorrow}; +use std::cell::Cell; use std::fmt; use std::hash::{Hash, Hasher}; use std::mem; @@ -270,17 +271,9 @@ impl<'ln> GeckoNode<'ln> { self.flags_atomic().fetch_or(flags, Ordering::Relaxed); } - #[inline] - fn flags_atomic(&self) -> &AtomicU32 { - use std::cell::Cell; - let flags: &Cell = &(self.0)._base._base_1.mFlags; - - #[allow(dead_code)] - fn static_assert() { - let _: [u8; std::mem::size_of::>()] = [0u8; std::mem::size_of::()]; - let _: [u8; std::mem::align_of::>()] = - [0u8; std::mem::align_of::()]; - } + fn flags_atomic_for(flags: &Cell) -> &AtomicU32 { + const_assert!(std::mem::size_of::>() == std::mem::size_of::()); + const_assert!(std::mem::align_of::>() == std::mem::align_of::()); // Rust doesn't provide standalone atomic functions like GCC/clang do // (via the atomic intrinsics) or via std::atomic_ref, but it guarantees @@ -289,11 +282,26 @@ impl<'ln> GeckoNode<'ln> { unsafe { std::mem::transmute::<&Cell, &AtomicU32>(flags) } } + #[inline] + fn flags_atomic(&self) -> &AtomicU32 { + Self::flags_atomic_for(&self.0._base._base_1.mFlags) + } + #[inline] fn flags(&self) -> u32 { self.flags_atomic().load(Ordering::Relaxed) } + #[inline] + fn selector_flags_atomic(&self) -> &AtomicU32 { + Self::flags_atomic_for(&self.0.mSelectorFlags) + } + + #[inline] + fn set_selector_flags(&self, flags: u32) { + self.selector_flags_atomic().fetch_or(flags, Ordering::Relaxed); + } + #[inline] fn node_info(&self) -> &structs::NodeInfo { debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null()); @@ -859,22 +867,22 @@ impl<'le> GeckoElement<'le> { /// by Gecko. We could align these and then do this without conditionals, but /// it's probably not worth the trouble. fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 { - use crate::gecko_bindings::structs::*; + use crate::gecko_bindings::structs::NodeSelectorFlags; let mut gecko_flags = 0u32; if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) { - gecko_flags |= NODE_HAS_SLOW_SELECTOR; + gecko_flags |= NodeSelectorFlags::HasSlowSelector.0; } if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) { - gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS; + gecko_flags |= NodeSelectorFlags::HasSlowSelectorLaterSiblings.0; } if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) { - gecko_flags |= NODE_HAS_SLOW_SELECTOR_NTH_OF; + gecko_flags |= NodeSelectorFlags::HasSlowSelectorNthOf.0; } if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) { - gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR; + gecko_flags |= NodeSelectorFlags::HasEdgeChildSelector.0; } if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) { - gecko_flags |= NODE_HAS_EMPTY_SELECTOR; + gecko_flags |= NodeSelectorFlags::HasEmptySelector.0; } gecko_flags @@ -1810,7 +1818,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { // Handle flags that apply to the element. let self_flags = flags.for_self(); if !self_flags.is_empty() { - self.set_flags(selector_flags_to_node_flags(flags)) + self.as_node().set_selector_flags(selector_flags_to_node_flags(flags)) } // Handle flags that apply to the parent. @@ -1818,7 +1826,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { if !parent_flags.is_empty() { if let Some(p) = self.as_node().parent_node() { if p.is_element() || p.is_shadow_root() { - p.set_flags(selector_flags_to_node_flags(parent_flags)); + p.set_selector_flags(selector_flags_to_node_flags(parent_flags)); } } }