forked from mirrors/gecko-dev
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
This commit is contained in:
parent
7998255fe4
commit
ec30a6eea6
7 changed files with 139 additions and 107 deletions
|
|
@ -231,7 +231,7 @@ ASSERT_NODE_SIZE(HTMLParagraphElement, 128, 80);
|
||||||
ASSERT_NODE_SIZE(HTMLPreElement, 128, 80);
|
ASSERT_NODE_SIZE(HTMLPreElement, 128, 80);
|
||||||
ASSERT_NODE_SIZE(HTMLSpanElement, 128, 80);
|
ASSERT_NODE_SIZE(HTMLSpanElement, 128, 80);
|
||||||
ASSERT_NODE_SIZE(HTMLTableCellElement, 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 ASSERT_NODE_SIZE
|
||||||
#undef EXTRA_DOM_NODE_BYTES
|
#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]",
|
fprintf(out, " state=[%llx]",
|
||||||
static_cast<unsigned long long>(State().GetInternalValue()));
|
static_cast<unsigned long long>(State().GetInternalValue()));
|
||||||
fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
|
fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
|
||||||
|
fprintf(out, " selectorflags=[%08x]",
|
||||||
|
static_cast<unsigned int>(GetSelectorFlags()));
|
||||||
if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
|
if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
|
||||||
const LinkedList<AbstractRange>* ranges =
|
const LinkedList<AbstractRange>* ranges =
|
||||||
GetExistingClosestCommonInclusiveAncestorRanges();
|
GetExistingClosestCommonInclusiveAncestorRanges();
|
||||||
|
|
|
||||||
|
|
@ -143,74 +143,82 @@ enum : uint32_t {
|
||||||
// Whether the node participates in a shadow tree.
|
// Whether the node participates in a shadow tree.
|
||||||
NODE_IS_IN_SHADOW_TREE = NODE_FLAG_BIT(5),
|
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 <selector>) or
|
|
||||||
// :nth-last-child(.. of <selector>). 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
|
// This node needs to go through frame construction to get a frame (or
|
||||||
// undisplayed entry).
|
// 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.
|
// 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
|
// 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(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.
|
// 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
|
// 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
|
// 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_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.
|
// 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.
|
// 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 <selector>) or
|
||||||
|
/// :nth-last-child(.. of <selector>). 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
|
// Make sure we have space for our bits
|
||||||
#define ASSERT_NODE_FLAGS_SPACE(n) \
|
#define ASSERT_NODE_FLAGS_SPACE(n) \
|
||||||
static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <= \
|
static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <= \
|
||||||
|
|
@ -2317,6 +2325,10 @@ class nsINode : public mozilla::dom::EventTarget {
|
||||||
#undef TOUCH_EVENT
|
#undef TOUCH_EVENT
|
||||||
#undef EVENT
|
#undef EVENT
|
||||||
|
|
||||||
|
NodeSelectorFlags GetSelectorFlags() const {
|
||||||
|
return static_cast<NodeSelectorFlags>(mSelectorFlags.Get());
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static bool Traverse(nsINode* tmp, nsCycleCollectionTraversalCallback& cb);
|
static bool Traverse(nsINode* tmp, nsCycleCollectionTraversalCallback& cb);
|
||||||
static void Unlink(nsINode* tmp);
|
static void Unlink(nsINode* tmp);
|
||||||
|
|
@ -2334,7 +2346,7 @@ class nsINode : public mozilla::dom::EventTarget {
|
||||||
uint32_t mBoolFlags;
|
uint32_t mBoolFlags;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE, there are 32 bits left here, at least in 64 bit builds.
|
mozilla::RustCell<uint32_t> mSelectorFlags{0};
|
||||||
|
|
||||||
uint32_t mChildCount;
|
uint32_t mChildCount;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,10 @@ class nsWindowRoot;
|
||||||
// Both sets of flags are 32 bits. On 64-bit platforms, this can cause two
|
// 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
|
// wasted 32-bit fields due to alignment requirements. Some compilers are
|
||||||
// smart enough to coalesce the fields if we make mBoolFlags the first member
|
// 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.
|
// So we just store mBoolFlags directly on nsWrapperCache on 64-bit platforms.
|
||||||
// This may waste space for some other nsWrapperCache-derived objects that have
|
// This may waste space for some other nsWrapperCache-derived objects that have
|
||||||
|
|
|
||||||
|
|
@ -94,17 +94,16 @@ void RestyleManager::ContentAppended(nsIContent* aFirstNewContent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
uint32_t selectorFlags =
|
const auto selectorFlags = container->GetSelectorFlags() &
|
||||||
container->GetFlags() &
|
NodeSelectorFlags::AllSimpleRestyleFlagsForAppend;
|
||||||
(NODE_RESTYLE_SELECTOR_FLAGS & ~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
|
if (!selectorFlags) {
|
||||||
if (selectorFlags == 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The container cannot be a document.
|
// The container cannot be a document.
|
||||||
MOZ_ASSERT(container->IsElement() || container->IsShadowRoot());
|
MOZ_ASSERT(container->IsElement() || container->IsShadowRoot());
|
||||||
|
|
||||||
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
|
if (selectorFlags & NodeSelectorFlags::HasEmptySelector) {
|
||||||
// see whether we need to restyle the container
|
// see whether we need to restyle the container
|
||||||
bool wasEmpty = true; // :empty or :-moz-only-whitespace
|
bool wasEmpty = true; // :empty or :-moz-only-whitespace
|
||||||
for (nsIContent* cur = container->GetFirstChild(); cur != aFirstNewContent;
|
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()) {
|
if (container->IsElement()) {
|
||||||
PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(),
|
PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(),
|
||||||
nsChangeHint(0));
|
nsChangeHint(0));
|
||||||
|
|
@ -136,7 +135,7 @@ void RestyleManager::ContentAppended(nsIContent* aFirstNewContent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) {
|
||||||
// restyle the last element child before this node
|
// restyle the last element child before this node
|
||||||
for (nsIContent* cur = aFirstNewContent->GetPreviousSibling(); cur;
|
for (nsIContent* cur = aFirstNewContent->GetPreviousSibling(); cur;
|
||||||
cur = cur->GetPreviousSibling()) {
|
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
|
// In some cases (:empty + E, :empty ~ E), a change in the content of
|
||||||
// an element requires restyling its parent's siblings.
|
// an element requires restyling its parent's siblings.
|
||||||
nsIContent* grandparent = aContainer->GetParent();
|
nsIContent* grandparent = aContainer->GetParent();
|
||||||
if (!grandparent ||
|
if (!grandparent || !(grandparent->GetSelectorFlags() &
|
||||||
!(grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
|
NodeSelectorFlags::HasSlowSelectorLaterSiblings)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RestyleSiblingsStartingWith(aContainer->GetNextSibling());
|
RestyleSiblingsStartingWith(aContainer->GetNextSibling());
|
||||||
|
|
@ -182,7 +181,8 @@ void RestyleManager::RestyleForEmptyChange(Element* aContainer) {
|
||||||
|
|
||||||
void RestyleManager::MaybeRestyleForEdgeChildChange(nsINode* aContainer,
|
void RestyleManager::MaybeRestyleForEdgeChildChange(nsINode* aContainer,
|
||||||
nsIContent* aChangedChild) {
|
nsIContent* aChangedChild) {
|
||||||
MOZ_ASSERT(aContainer->GetFlags() & NODE_HAS_EDGE_CHILD_SELECTOR);
|
MOZ_ASSERT(aContainer->GetSelectorFlags() &
|
||||||
|
NodeSelectorFlags::HasEdgeChildSelector);
|
||||||
MOZ_ASSERT(aChangedChild->GetParent() == aContainer);
|
MOZ_ASSERT(aChangedChild->GetParent() == aContainer);
|
||||||
// restyle the previously-first element child if it is after this node
|
// restyle the previously-first element child if it is after this node
|
||||||
bool passedChild = false;
|
bool passedChild = false;
|
||||||
|
|
@ -264,9 +264,10 @@ void RestyleManager::CharacterDataChanged(
|
||||||
nsINode* parent = aContent->GetParentNode();
|
nsINode* parent = aContent->GetParentNode();
|
||||||
MOZ_ASSERT(parent, "How were we notified of a stray node?");
|
MOZ_ASSERT(parent, "How were we notified of a stray node?");
|
||||||
|
|
||||||
uint32_t slowSelectorFlags = parent->GetFlags() & NODE_RESTYLE_SELECTOR_FLAGS;
|
const auto slowSelectorFlags =
|
||||||
if (!(slowSelectorFlags &
|
parent->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags;
|
||||||
(NODE_HAS_EMPTY_SELECTOR | NODE_HAS_EDGE_CHILD_SELECTOR))) {
|
if (!(slowSelectorFlags & (NodeSelectorFlags::HasEmptySelector |
|
||||||
|
NodeSelectorFlags::HasEdgeChildSelector))) {
|
||||||
// Nothing to do, no other slow selector can change as a result of this.
|
// Nothing to do, no other slow selector can change as a result of this.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +322,7 @@ void RestyleManager::CharacterDataChanged(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slowSelectorFlags & NODE_HAS_EMPTY_SELECTOR) {
|
if (slowSelectorFlags & NodeSelectorFlags::HasEmptySelector) {
|
||||||
if (!HasAnySignificantSibling(parent->AsElement(), aContent)) {
|
if (!HasAnySignificantSibling(parent->AsElement(), aContent)) {
|
||||||
// We used to be empty, restyle the parent.
|
// We used to be empty, restyle the parent.
|
||||||
RestyleForEmptyChange(parent->AsElement());
|
RestyleForEmptyChange(parent->AsElement());
|
||||||
|
|
@ -329,7 +330,7 @@ void RestyleManager::CharacterDataChanged(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slowSelectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
if (slowSelectorFlags & NodeSelectorFlags::HasEdgeChildSelector) {
|
||||||
MaybeRestyleForEdgeChildChange(parent, aContent);
|
MaybeRestyleForEdgeChildChange(parent, aContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -344,8 +345,9 @@ void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) {
|
||||||
nsINode* container = aChild->GetParentNode();
|
nsINode* container = aChild->GetParentNode();
|
||||||
MOZ_ASSERT(container);
|
MOZ_ASSERT(container);
|
||||||
|
|
||||||
uint32_t selectorFlags = container->GetFlags() & NODE_RESTYLE_SELECTOR_FLAGS;
|
const auto selectorFlags =
|
||||||
if (selectorFlags == 0) {
|
container->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags;
|
||||||
|
if (!selectorFlags) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,7 +357,8 @@ void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) {
|
||||||
// The container cannot be a document.
|
// The container cannot be a document.
|
||||||
MOZ_ASSERT(container->IsElement() || container->IsShadowRoot());
|
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 /
|
// See whether we need to restyle the container due to :empty /
|
||||||
// :-moz-only-whitespace.
|
// :-moz-only-whitespace.
|
||||||
const bool wasEmpty =
|
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()) {
|
if (container->IsElement()) {
|
||||||
PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(),
|
PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(),
|
||||||
nsChangeHint(0));
|
nsChangeHint(0));
|
||||||
|
|
@ -381,12 +384,12 @@ void RestyleManager::RestyleForInsertOrChange(nsIContent* aChild) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
|
if (selectorFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings) {
|
||||||
// Restyle all later siblings.
|
// Restyle all later siblings.
|
||||||
RestyleSiblingsStartingWith(aChild->GetNextSibling());
|
RestyleSiblingsStartingWith(aChild->GetNextSibling());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) {
|
||||||
MaybeRestyleForEdgeChildChange(container, aChild);
|
MaybeRestyleForEdgeChildChange(container, aChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -408,8 +411,9 @@ void RestyleManager::ContentRemoved(nsIContent* aOldChild,
|
||||||
IncrementUndisplayedRestyleGeneration();
|
IncrementUndisplayedRestyleGeneration();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t selectorFlags = container->GetFlags() & NODE_RESTYLE_SELECTOR_FLAGS;
|
const auto selectorFlags =
|
||||||
if (selectorFlags == 0) {
|
container->GetSelectorFlags() & NodeSelectorFlags::AllSimpleRestyleFlags;
|
||||||
|
if (!selectorFlags) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,7 +428,8 @@ void RestyleManager::ContentRemoved(nsIContent* aOldChild,
|
||||||
// The container cannot be a document.
|
// The container cannot be a document.
|
||||||
MOZ_ASSERT(container->IsElement() || container->IsShadowRoot());
|
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
|
// see whether we need to restyle the container
|
||||||
bool isEmpty = true; // :empty or :-moz-only-whitespace
|
bool isEmpty = true; // :empty or :-moz-only-whitespace
|
||||||
for (nsIContent* child = container->GetFirstChild(); child;
|
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()) {
|
if (container->IsElement()) {
|
||||||
PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(),
|
PostRestyleEvent(container->AsElement(), RestyleHint::RestyleSubtree(),
|
||||||
nsChangeHint(0));
|
nsChangeHint(0));
|
||||||
|
|
@ -456,12 +461,12 @@ void RestyleManager::ContentRemoved(nsIContent* aOldChild,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
|
if (selectorFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings) {
|
||||||
// Restyle all later siblings.
|
// Restyle all later siblings.
|
||||||
RestyleSiblingsStartingWith(aFollowingSibling);
|
RestyleSiblingsStartingWith(aFollowingSibling);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
if (selectorFlags & NodeSelectorFlags::HasEdgeChildSelector) {
|
||||||
// restyle the now-first element child if it was after aOldChild
|
// restyle the now-first element child if it was after aOldChild
|
||||||
bool reachedFollowingSibling = false;
|
bool reachedFollowingSibling = false;
|
||||||
for (nsIContent* content = container->GetFirstChild(); content;
|
for (nsIContent* content = container->GetFirstChild(); content;
|
||||||
|
|
@ -3355,8 +3360,8 @@ void RestyleManager::MaybeRestyleForNthOfState(ServoStyleSet& aStyleSet,
|
||||||
ElementState aChangedBits) {
|
ElementState aChangedBits) {
|
||||||
const auto* parentNode = aChild->GetParentNode();
|
const auto* parentNode = aChild->GetParentNode();
|
||||||
MOZ_ASSERT(parentNode);
|
MOZ_ASSERT(parentNode);
|
||||||
const auto parentFlags = parentNode->GetFlags();
|
const auto parentFlags = parentNode->GetSelectorFlags();
|
||||||
if (!(parentFlags & NODE_HAS_SLOW_SELECTOR_NTH_OF)) {
|
if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3535,20 +3540,21 @@ void RestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestyleManager::RestyleSiblings(
|
void RestyleManager::RestyleSiblings(Element* aChild,
|
||||||
Element* aChild, nsBaseContentList::FlagsType aParentFlags) {
|
NodeSelectorFlags aParentFlags) {
|
||||||
const DebugOnly<nsINode*> parentNode = aChild->GetParentNode();
|
const DebugOnly<nsINode*> parentNode = aChild->GetParentNode();
|
||||||
MOZ_ASSERT(parentNode->IsElement() || parentNode->IsShadowRoot());
|
MOZ_ASSERT(parentNode->IsElement() || parentNode->IsShadowRoot());
|
||||||
|
|
||||||
DebugOnly<bool> restyledSiblings = false;
|
DebugOnly<bool> restyledSiblings = false;
|
||||||
// NODE_HAS_SLOW_SELECTOR typically indicates restyling the parent, but since
|
// NodeSelectorFlags::HasSlowSelector typically indicates restyling the
|
||||||
// we know we're restyling for :nth-last-child(.. of <selector>), we can
|
// parent, but since we know we're restyling for :nth-last-child(.. of
|
||||||
// restyle only previous siblings without under-invalidating.
|
// <selector>), we can restyle only previous siblings without
|
||||||
if (aParentFlags & NODE_HAS_SLOW_SELECTOR) {
|
// under-invalidating.
|
||||||
|
if (aParentFlags & NodeSelectorFlags::HasSlowSelector) {
|
||||||
RestylePreviousSiblings(aChild->GetPreviousSibling());
|
RestylePreviousSiblings(aChild->GetPreviousSibling());
|
||||||
restyledSiblings = true;
|
restyledSiblings = true;
|
||||||
}
|
}
|
||||||
if (aParentFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
|
if (aParentFlags & NodeSelectorFlags::HasSlowSelectorLaterSiblings) {
|
||||||
RestyleSiblingsStartingWith(aChild->GetNextSibling());
|
RestyleSiblingsStartingWith(aChild->GetNextSibling());
|
||||||
restyledSiblings = true;
|
restyledSiblings = true;
|
||||||
}
|
}
|
||||||
|
|
@ -3560,8 +3566,8 @@ void RestyleManager::MaybeRestyleForNthOfAttribute(
|
||||||
Element* aChild, nsAtom* aAttribute, const nsAttrValue* aOldValue) {
|
Element* aChild, nsAtom* aAttribute, const nsAttrValue* aOldValue) {
|
||||||
const auto* parentNode = aChild->GetParentNode();
|
const auto* parentNode = aChild->GetParentNode();
|
||||||
MOZ_ASSERT(parentNode);
|
MOZ_ASSERT(parentNode);
|
||||||
const auto parentFlags = parentNode->GetFlags();
|
const auto parentFlags = parentNode->GetSelectorFlags();
|
||||||
if (!(parentFlags & NODE_HAS_SLOW_SELECTOR_NTH_OF)) {
|
if (!(parentFlags & NodeSelectorFlags::HasSlowSelectorNthOf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!aChild->HasServoData()) {
|
if (!aChild->HasServoData()) {
|
||||||
|
|
|
||||||
|
|
@ -379,8 +379,7 @@ class RestyleManager {
|
||||||
/**
|
/**
|
||||||
* Restyle an element's previous and/or next siblings.
|
* Restyle an element's previous and/or next siblings.
|
||||||
*/
|
*/
|
||||||
void RestyleSiblings(dom::Element* aChild,
|
void RestyleSiblings(dom::Element* aChild, NodeSelectorFlags aParentFlags);
|
||||||
nsBaseContentList::FlagsType aParentFlags);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts restyle hints for siblings of an element and their descendants if the
|
* Posts restyle hints for siblings of an element and their descendants if the
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ hide-types = [
|
||||||
bitfield-enums = [
|
bitfield-enums = [
|
||||||
"nsChangeHint",
|
"nsChangeHint",
|
||||||
"mozilla::OriginFlags",
|
"mozilla::OriginFlags",
|
||||||
|
"NodeSelectorFlags",
|
||||||
]
|
]
|
||||||
rusty-enums = [
|
rusty-enums = [
|
||||||
"nsCompatibility",
|
"nsCompatibility",
|
||||||
|
|
@ -354,6 +355,7 @@ allowlist-types = [
|
||||||
"mozilla::dom::MediaList",
|
"mozilla::dom::MediaList",
|
||||||
"mozilla::StyleRuleInclusion",
|
"mozilla::StyleRuleInclusion",
|
||||||
"nsStyleTransformMatrix::MatrixTransformOperator",
|
"nsStyleTransformMatrix::MatrixTransformOperator",
|
||||||
|
"NodeSelectorFlags",
|
||||||
]
|
]
|
||||||
opaque-types = [
|
opaque-types = [
|
||||||
"mozilla::StyleThinArc", # https://github.com/rust-lang/rust-bindgen/issues/1557
|
"mozilla::StyleThinArc", # https://github.com/rust-lang/rust-bindgen/issues/1557
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ use selectors::matching::{ElementSelectorFlags, MatchingContext};
|
||||||
use selectors::sink::Push;
|
use selectors::sink::Push;
|
||||||
use selectors::{Element, OpaqueElement};
|
use selectors::{Element, OpaqueElement};
|
||||||
use servo_arc::{Arc, ArcBorrow};
|
use servo_arc::{Arc, ArcBorrow};
|
||||||
|
use std::cell::Cell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
@ -270,17 +271,9 @@ impl<'ln> GeckoNode<'ln> {
|
||||||
self.flags_atomic().fetch_or(flags, Ordering::Relaxed);
|
self.flags_atomic().fetch_or(flags, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn flags_atomic_for(flags: &Cell<u32>) -> &AtomicU32 {
|
||||||
fn flags_atomic(&self) -> &AtomicU32 {
|
const_assert!(std::mem::size_of::<Cell<u32>>() == std::mem::size_of::<AtomicU32>());
|
||||||
use std::cell::Cell;
|
const_assert!(std::mem::align_of::<Cell<u32>>() == std::mem::align_of::<AtomicU32>());
|
||||||
let flags: &Cell<u32> = &(self.0)._base._base_1.mFlags;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn static_assert() {
|
|
||||||
let _: [u8; std::mem::size_of::<Cell<u32>>()] = [0u8; std::mem::size_of::<AtomicU32>()];
|
|
||||||
let _: [u8; std::mem::align_of::<Cell<u32>>()] =
|
|
||||||
[0u8; std::mem::align_of::<AtomicU32>()];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rust doesn't provide standalone atomic functions like GCC/clang do
|
// Rust doesn't provide standalone atomic functions like GCC/clang do
|
||||||
// (via the atomic intrinsics) or via std::atomic_ref, but it guarantees
|
// (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<u32>, &AtomicU32>(flags) }
|
unsafe { std::mem::transmute::<&Cell<u32>, &AtomicU32>(flags) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn flags_atomic(&self) -> &AtomicU32 {
|
||||||
|
Self::flags_atomic_for(&self.0._base._base_1.mFlags)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn flags(&self) -> u32 {
|
fn flags(&self) -> u32 {
|
||||||
self.flags_atomic().load(Ordering::Relaxed)
|
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]
|
#[inline]
|
||||||
fn node_info(&self) -> &structs::NodeInfo {
|
fn node_info(&self) -> &structs::NodeInfo {
|
||||||
debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null());
|
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
|
/// by Gecko. We could align these and then do this without conditionals, but
|
||||||
/// it's probably not worth the trouble.
|
/// it's probably not worth the trouble.
|
||||||
fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {
|
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;
|
let mut gecko_flags = 0u32;
|
||||||
if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
|
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) {
|
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) {
|
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) {
|
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) {
|
if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) {
|
||||||
gecko_flags |= NODE_HAS_EMPTY_SELECTOR;
|
gecko_flags |= NodeSelectorFlags::HasEmptySelector.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
gecko_flags
|
gecko_flags
|
||||||
|
|
@ -1810,7 +1818,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
// Handle flags that apply to the element.
|
// Handle flags that apply to the element.
|
||||||
let self_flags = flags.for_self();
|
let self_flags = flags.for_self();
|
||||||
if !self_flags.is_empty() {
|
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.
|
// Handle flags that apply to the parent.
|
||||||
|
|
@ -1818,7 +1826,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
if !parent_flags.is_empty() {
|
if !parent_flags.is_empty() {
|
||||||
if let Some(p) = self.as_node().parent_node() {
|
if let Some(p) = self.as_node().parent_node() {
|
||||||
if p.is_element() || p.is_shadow_root() {
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue