Bug 860573 - Part 2 - Use spare bits of wrapper cache flags for nsINode r=smaug
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -99,16 +99,19 @@ enum {
ELEMENT_HAS_PENDING_ANIMATION_RESTYLE,
// Remaining bits are for subclasses
ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 4
};
#undef ELEMENT_FLAG_BIT
+// Make sure we have space for our bits
+ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET);
+
namespace mozilla {
namespace dom {
class Link;
class UndoManager;
// IID for the dom::Element interface
#define NS_ELEMENT_IID \
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -66,17 +66,17 @@ template<typename T> class Optional;
} // namespace dom
} // namespace mozilla
namespace JS {
class Value;
template<typename T> class Handle;
}
-#define NODE_FLAG_BIT(n_) (1U << (n_))
+#define NODE_FLAG_BIT(n_) (1U << (WRAPPER_CACHE_FLAGS_BITS_USED + (n_)))
enum {
// This bit will be set if the node has a listener manager.
NODE_HAS_LISTENERMANAGER = NODE_FLAG_BIT(0),
// Whether this node has had any properties set on it
NODE_HAS_PROPERTIES = NODE_FLAG_BIT(1),
@@ -150,39 +150,37 @@ enum {
// 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(16),
// Set if the node has the accesskey attribute set.
NODE_HAS_ACCESSKEY = NODE_FLAG_BIT(17),
- // Set if the node is handling a click.
- NODE_HANDLING_CLICK = NODE_FLAG_BIT(18),
-
- // Set if the node has had :hover selectors matched against it
- NODE_HAS_RELEVANT_HOVER_RULES = NODE_FLAG_BIT(19),
-
// Set if the node has right-to-left directionality
- NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(20),
+ NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(18),
// Set if the node has left-to-right directionality
- NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(21),
+ NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(19),
NODE_ALL_DIRECTION_FLAGS = NODE_HAS_DIRECTION_LTR |
NODE_HAS_DIRECTION_RTL,
- NODE_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(22),
+ NODE_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(20),
- NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(23),
+ NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(21),
// Remaining bits are node type specific.
- NODE_TYPE_SPECIFIC_BITS_OFFSET = 24
+ NODE_TYPE_SPECIFIC_BITS_OFFSET = 22
};
+// Make sure we have space for our bits
+#define ASSERT_NODE_FLAGS_SPACE(n) PR_STATIC_ASSERT(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <= 32)
+ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET);
+
/**
* Class used to detect unexpected mutations. To use the class create an
* nsMutationGuard on the stack before unexpected mutations could occur.
* You can then at any time call Mutated to check if any unexpected mutations
* have occurred.
*
* When a guard is instantiated sMutationCount is set to 300. It is then
* decremented by every mutation (capped at 0). This means that we can only
@@ -314,17 +312,16 @@ public:
#ifdef _MSC_VER
#pragma warning(push)
// Disable annoying warning about 'this' in initializers.
#pragma warning(disable:4355)
#endif
nsINode(already_AddRefed<nsINodeInfo> aNodeInfo)
: mNodeInfo(aNodeInfo),
mParent(nullptr),
- mFlags(0),
mBoolFlags(0),
mNextSibling(nullptr),
mPreviousSibling(nullptr),
mFirstChild(nullptr),
mSubtreeRoot(this),
mSlots(nullptr)
{
}
@@ -926,48 +923,38 @@ public:
*/
#ifdef DEBUG
nsSlots* DebugGetSlots()
{
return Slots();
}
#endif
- bool HasFlag(uintptr_t aFlag) const
- {
- return !!(GetFlags() & aFlag);
- }
-
- uint32_t GetFlags() const
- {
- return mFlags;
- }
-
void SetFlags(uint32_t aFlagsToSet)
{
NS_ASSERTION(!(aFlagsToSet & (NODE_IS_ANONYMOUS |
NODE_IS_NATIVE_ANONYMOUS_ROOT |
NODE_IS_IN_ANONYMOUS_SUBTREE |
NODE_ATTACH_BINDING_ON_POSTCREATE |
NODE_DESCENDANTS_NEED_FRAMES |
NODE_NEEDS_FRAME |
NODE_CHROME_ONLY_ACCESS)) ||
IsNodeOfType(eCONTENT),
"Flag only permitted on nsIContent nodes");
- mFlags |= aFlagsToSet;
+ nsWrapperCache::SetFlags(aFlagsToSet);
}
void UnsetFlags(uint32_t aFlagsToUnset)
{
NS_ASSERTION(!(aFlagsToUnset &
(NODE_IS_ANONYMOUS |
NODE_IS_IN_ANONYMOUS_SUBTREE |
NODE_IS_NATIVE_ANONYMOUS_ROOT)),
"Trying to unset write-only flags");
- mFlags &= ~aFlagsToUnset;
+ nsWrapperCache::UnsetFlags(aFlagsToUnset);
}
void SetEditableFlag(bool aEditable)
{
if (aEditable) {
SetFlags(NODE_IS_EDITABLE);
}
else {
@@ -1332,16 +1319,20 @@ private:
NodeHasDirAuto,
// Set if a node in the node's parent chain has dir=auto.
NodeAncestorHasDirAuto,
// Set if the element is in the scope of a scoped style sheet; this flag is
// only accurate for elements bounds to a document
ElementIsInStyleScope,
// Set if the element is a scoped style sheet root
ElementIsScopedStyleRoot,
+ // Set if the node is handling a click.
+ NodeHandlingClick,
+ // Set if the node has had :hover selectors matched against it
+ NodeHasRelevantHoverRules,
// Guard value
BooleanFlagCount
};
void SetBoolFlag(BooleanFlag name, bool value) {
PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
}
@@ -1466,16 +1457,18 @@ public:
MOZ_ASSERT(IsElement(), "ClearIsInStyleScope on a non-Element node");
ClearBoolFlag(ElementIsInStyleScope);
}
bool IsElementInStyleScope() const { return GetBoolFlag(ElementIsInStyleScope); }
void SetIsScopedStyleRoot() { SetBoolFlag(ElementIsScopedStyleRoot); }
void ClearIsScopedStyleRoot() { ClearBoolFlag(ElementIsScopedStyleRoot); }
bool IsScopedStyleRoot() { return GetBoolFlag(ElementIsScopedStyleRoot); }
+ bool HasRelevantHoverRules() const { return GetBoolFlag(NodeHasRelevantHoverRules); }
+ void SetHasRelevantHoverRules() { SetBoolFlag(NodeHasRelevantHoverRules); }
protected:
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
void SetInDocument() { SetBoolFlag(IsInDocument); }
void SetNodeIsContent() { SetBoolFlag(NodeIsContent); }
void ClearInDocument() { ClearBoolFlag(IsInDocument); }
void SetIsElement() { SetBoolFlag(NodeIsElement); }
void SetHasID() { SetBoolFlag(ElementHasID); }
void ClearHasID() { ClearBoolFlag(ElementHasID); }
@@ -1485,16 +1478,19 @@ protected:
void SetMayHaveContentEditableAttr()
{ SetBoolFlag(ElementMayHaveContentEditableAttr); }
bool HasExplicitBaseURI() const { return GetBoolFlag(NodeHasExplicitBaseURI); }
void SetHasExplicitBaseURI() { SetBoolFlag(NodeHasExplicitBaseURI); }
void SetHasLockedStyleStates() { SetBoolFlag(ElementHasLockedStyleStates); }
void ClearHasLockedStyleStates() { ClearBoolFlag(ElementHasLockedStyleStates); }
bool HasLockedStyleStates() const
{ return GetBoolFlag(ElementHasLockedStyleStates); }
+ bool HandlingClick() const { return GetBoolFlag(NodeHandlingClick); }
+ void SetHandlingClick() { SetBoolFlag(NodeHandlingClick); }
+ void ClearHandlingClick() { ClearBoolFlag(NodeHandlingClick); }
void SetSubtreeRootPointer(nsINode* aSubtreeRoot)
{
NS_ASSERTION(aSubtreeRoot, "aSubtreeRoot can never be null!");
NS_ASSERTION(!(IsNodeOfType(eCONTENT) && IsInDoc()), "Shouldn't be here!");
mSubtreeRoot = aSubtreeRoot;
}
@@ -1733,18 +1729,16 @@ public:
protected:
static bool Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb);
static void Unlink(nsINode *tmp);
nsCOMPtr<nsINodeInfo> mNodeInfo;
nsINode* mParent;
- uint32_t mFlags;
-
private:
// Boolean flags.
uint32_t mBoolFlags;
protected:
nsIContent* mNextSibling;
nsIContent* mPreviousSibling;
nsIContent* mFirstChild;
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -40,17 +40,17 @@ enum {
NS_CREATE_FRAME_IF_NON_WHITESPACE = DATA_NODE_FLAG_BIT(0),
// This bit is set to indicate that if the text node changes to
// whitespace, we may need to reframe it (or its ancestors).
NS_REFRAME_IF_WHITESPACE = DATA_NODE_FLAG_BIT(1)
};
// Make sure we have enough space for those bits
-PR_STATIC_ASSERT(NODE_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32);
+ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET + 2);
#undef DATA_NODE_FLAG_BIT
class nsGenericDOMDataNode : public nsIContent
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
--- a/content/html/content/src/HTMLAnchorElement.cpp
+++ b/content/html/content/src/HTMLAnchorElement.cpp
@@ -26,18 +26,17 @@ namespace dom {
enum {
// Indicates that a DNS Prefetch has been requested from this Anchor elem
HTML_ANCHOR_DNS_PREFETCH_REQUESTED = ANCHOR_ELEMENT_FLAG_BIT(0),
// Indicates that a DNS Prefetch was added to the deferral queue
HTML_ANCHOR_DNS_PREFETCH_DEFERRED = ANCHOR_ELEMENT_FLAG_BIT(1)
};
-// Make sure we have enough space for those bits
-PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32);
+ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
#undef ANCHOR_ELEMENT_FLAG_BIT
HTMLAnchorElement::~HTMLAnchorElement()
{
}
NS_IMPL_ADDREF_INHERITED(HTMLAnchorElement, Element)
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2803,43 +2803,43 @@ nsGenericHTMLElement::Focus(ErrorResult&
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(this);
aError = fm->SetFocus(elem, 0);
}
}
void
nsGenericHTMLElement::Click()
{
- if (HasFlag(NODE_HANDLING_CLICK))
+ if (HandlingClick())
return;
// Strong in case the event kills it
nsCOMPtr<nsIDocument> doc = GetCurrentDoc();
nsCOMPtr<nsIPresShell> shell;
nsRefPtr<nsPresContext> context;
if (doc) {
shell = doc->GetShell();
if (shell) {
context = shell->GetPresContext();
}
}
- SetFlags(NODE_HANDLING_CLICK);
+ SetHandlingClick();
// Click() is never called from native code, but it may be
// called from chrome JS. Mark this event trusted if Click()
// is called from chrome code.
nsMouseEvent event(nsContentUtils::IsCallerChrome(),
NS_MOUSE_CLICK, nullptr, nsMouseEvent::eReal);
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
nsEventDispatcher::Dispatch(this, context, &event);
- UnsetFlags(NODE_HANDLING_CLICK);
+ ClearHandlingClick();
}
bool
nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse,
bool *aIsFocusable,
int32_t *aTabIndex)
{
nsIDocument *doc = GetCurrentDoc();
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1058,18 +1058,17 @@ enum {
// hasn't re-found its form in nsGenericHTMLFormElement::UnbindFromTree yet.
MAYBE_ORPHAN_FORM_ELEMENT = FORM_ELEMENT_FLAG_BIT(1)
};
// NOTE: I don't think it's possible to have the above two flags set at the
// same time, so if it becomes an issue we can probably merge them into the
// same bit. --bz
-// Make sure we have enough space for those bits
-PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32);
+ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
#undef FORM_ELEMENT_FLAG_BIT
/**
* A helper class for form elements that can contain children
*/
class nsGenericHTMLFormElement : public nsGenericHTMLElement,
public nsIFormControl
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -339,18 +339,17 @@ public:
// XUL element specific bits
enum {
XUL_ELEMENT_TEMPLATE_GENERATED = XUL_ELEMENT_FLAG_BIT(0),
XUL_ELEMENT_HAS_CONTENTMENU_LISTENER = XUL_ELEMENT_FLAG_BIT(1),
XUL_ELEMENT_HAS_POPUP_LISTENER = XUL_ELEMENT_FLAG_BIT(2)
};
-// Make sure we have space for our bits
-PR_STATIC_ASSERT((ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2) < 32);
+ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 3);
#undef XUL_ELEMENT_FLAG_BIT
class nsScriptEventHandlerOwnerTearoff;
class nsXULElement : public nsStyledElement,
public nsIDOMXULElement
{
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -2096,17 +2096,17 @@ static bool SelectorMatches(Element* aEl
!IsQuirkEventSensitive(aElement->Tag())) {
// In quirks mode, only make certain elements sensitive to
// selectors ":hover" and ":active".
return false;
} else {
if (aTreeMatchContext.mForStyling &&
statesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER)) {
// Mark the element as having :hover-dependent style
- aElement->SetFlags(NODE_HAS_RELEVANT_HOVER_RULES);
+ aElement->SetHasRelevantHoverRules();
}
if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(statesToCheck)) {
if (aDependence)
*aDependence = true;
} else {
nsEventStates contentState =
nsCSSRuleProcessor::GetContentStateForVisitedHandling(
aElement,
@@ -2487,24 +2487,24 @@ nsCSSRuleProcessor::HasStateDependentSty
// don't bother calling SelectorMatches, since even if it returns false
// hint won't change.
// Also don't bother calling SelectorMatches if none of the
// states passed in are relevant here.
if ((possibleChange & ~hint) &&
states.HasAtLeastOneOfStates(aData->mStateMask) &&
// We can optimize away testing selectors that only involve :hover, a
// namespace, and a tag name against nodes that don't have the
- // NODE_HAS_RELEVANT_HOVER_RULES flag: such a selector didn't match
+ // NodeHasRelevantHoverRules flag: such a selector didn't match
// the tag name or namespace the first time around (since the :hover
- // didn't set the NODE_HAS_RELEVANT_HOVER_RULES flag), so it won't
+ // didn't set the NodeHasRelevantHoverRules flag), so it won't
// match it now. Check for our selector only having :hover states, or
// the element having the hover rules flag, or the selector having
// some sort of non-namespace, non-tagname data in it.
(states != NS_EVENT_STATE_HOVER ||
- aData->mElement->HasFlag(NODE_HAS_RELEVANT_HOVER_RULES) ||
+ aData->mElement->HasRelevantHoverRules() ||
selector->mIDList || selector->mClassList ||
// We generally expect an mPseudoClassList, since we have a :hover.
// The question is whether we have anything else in there.
(selector->mPseudoClassList &&
(selector->mPseudoClassList->mNext ||
selector->mPseudoClassList->mType !=
nsCSSPseudoClasses::ePseudoClass_hover)) ||
selector->mAttrList || selector->mNegations) &&