Bug 1373798 part 2. Introduce event state flags that track the state of an element's "dir" attribute. r=mystor
MozReview-Commit-ID: EDCV2fUWGmX
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -577,16 +577,26 @@ protected:
}
virtual void RemoveStates(EventStates aStates)
{
NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES),
"Should only be removing externally-managed states here");
RemoveStatesSilently(aStates);
NotifyStateChange(aStates);
}
+ virtual void ToggleStates(EventStates aStates, bool aNotify)
+ {
+ NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES),
+ "Should only be removing externally-managed states here");
+ mState ^= aStates;
+ if (aNotify) {
+ NotifyStateChange(aStates);
+ }
+ }
+
public:
// Public methods to manage state bits in MANUALLY_MANAGED_STATES.
void AddManuallyManagedStates(EventStates aStates)
{
MOZ_ASSERT(MANUALLY_MANAGED_STATES.HasAllStates(aStates),
"Should only be adding manually-managed states here");
AddStates(aStates);
}
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -283,30 +283,56 @@ private:
// Handler for click to play plugin (vulnerable w/no update)
#define NS_EVENT_STATE_VULNERABLE_NO_UPDATE NS_DEFINE_EVENT_STATE_MACRO(42)
// Element has focus-within.
#define NS_EVENT_STATE_FOCUS_WITHIN NS_DEFINE_EVENT_STATE_MACRO(43)
// Element is ltr (for :dir pseudo-class)
#define NS_EVENT_STATE_LTR NS_DEFINE_EVENT_STATE_MACRO(44)
// Element is rtl (for :dir pseudo-class)
#define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(45)
+// States for tracking the state of the "dir" attribute for HTML elements. We
+// use these to avoid having to get "dir" attributes all the time during
+// selector matching for some parts of the UA stylesheet.
+//
+// These states are externally managed, because we also don't want to keep
+// getting "dir" attributes in IntrinsicState.
+//
+// Element is HTML and has a "dir" attibute. This state might go away depending
+// on how https://github.com/whatwg/html/issues/2769 gets resolved. The value
+// could be anything.
+#define NS_EVENT_STATE_HAS_DIR_ATTR NS_DEFINE_EVENT_STATE_MACRO(46)
+// Element is HTML, has a "dir" attribute, and the attribute's value is
+// case-insensitively equal to "ltr".
+#define NS_EVENT_STATE_DIR_ATTR_LTR NS_DEFINE_EVENT_STATE_MACRO(47)
+// Element is HTML, has a "dir" attribute, and the attribute's value is
+// case-insensitively equal to "rtl".
+#define NS_EVENT_STATE_DIR_ATTR_RTL NS_DEFINE_EVENT_STATE_MACRO(48)
+// Element is HTML, and is either a <bdi> element with no valid-valued "dir"
+// attribute or any HTML element which has a "dir" attribute whose value is
+// "auto".
+#define NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO NS_DEFINE_EVENT_STATE_MACRO(49)
// Element is filled by Autofill feature.
#define NS_EVENT_STATE_AUTOFILL NS_DEFINE_EVENT_STATE_MACRO(50)
// Element is filled with preview data by Autofill feature.
#define NS_EVENT_STATE_AUTOFILL_PREVIEW NS_DEFINE_EVENT_STATE_MACRO(51)
// Event state that is used for values that need to be parsed but do nothing.
#define NS_EVENT_STATE_IGNORE NS_DEFINE_EVENT_STATE_MACRO(63)
/**
* NOTE: do not go over 63 without updating EventStates::InternalType!
*/
#define DIRECTION_STATES (NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL)
+#define DIR_ATTR_STATES (NS_EVENT_STATE_HAS_DIR_ATTR | \
+ NS_EVENT_STATE_DIR_ATTR_LTR | \
+ NS_EVENT_STATE_DIR_ATTR_RTL | \
+ NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO)
+
// Event states that can be added and removed through
// Element::{Add,Remove}ManuallyManagedStates.
//
// Take care when manually managing state bits. You are responsible for
// setting or clearing the bit when an Element is added or removed from a
// document (e.g. in BindToTree and UnbindFromTree), if that is an
// appropriate thing to do for your state bit.
#define MANUALLY_MANAGED_STATES ( \
@@ -315,16 +341,17 @@ private:
)
// Event states that are managed externally to an element (by the
// EventStateManager, or by other code). As opposed to those in
// INTRINSIC_STATES, which are are computed by the element itself
// and returned from Element::IntrinsicState.
#define EXTERNALLY_MANAGED_STATES ( \
MANUALLY_MANAGED_STATES | \
+ DIR_ATTR_STATES | \
NS_EVENT_STATE_ACTIVE | \
NS_EVENT_STATE_DRAGOVER | \
NS_EVENT_STATE_FOCUS | \
NS_EVENT_STATE_FOCUSRING | \
NS_EVENT_STATE_FOCUS_WITHIN | \
NS_EVENT_STATE_FULL_SCREEN | \
NS_EVENT_STATE_HOVER | \
NS_EVENT_STATE_UNRESOLVED | \
--- a/dom/html/HTMLUnknownElement.h
+++ b/dom/html/HTMLUnknownElement.h
@@ -2,16 +2,17 @@
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_HTMLUnknownElement_h
#define mozilla_dom_HTMLUnknownElement_h
#include "mozilla/Attributes.h"
+#include "mozilla/EventStates.h"
#include "nsGenericHTMLElement.h"
namespace mozilla {
namespace dom {
#define NS_HTMLUNKNOWNELEMENT_IID \
{ 0xc09e665b, 0x3876, 0x40dd, \
{ 0x85, 0x28, 0x44, 0xc2, 0x3f, 0xd4, 0x58, 0xf2 } }
@@ -22,16 +23,17 @@ public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTMLUNKNOWNELEMENT_IID)
NS_DECL_ISUPPORTS_INHERITED
explicit HTMLUnknownElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
: nsGenericHTMLElement(aNodeInfo)
{
if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
+ AddStatesSilently(NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO);
SetHasDirAuto();
}
}
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
bool aPreallocateChildren) const override;
protected:
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -721,38 +721,66 @@ nsGenericHTMLElement::AfterSetAttr(int32
nsresult rv = SetEventHandler(aName, aValue->GetStringValue());
NS_ENSURE_SUCCESS(rv, rv);
}
else if (aNotify && aName == nsGkAtoms::spellcheck) {
SyncEditorsOnSubtree(this);
}
else if (aName == nsGkAtoms::dir) {
Directionality dir = eDir_LTR;
+ // A boolean tracking whether we need to recompute our directionality.
+ // This needs to happen after we update our internal "dir" attribute
+ // state but before we call SetDirectionalityOnDescendants.
+ bool recomputeDirectionality = false;
+ // We don't want to have to keep getting the "dir" attribute in
+ // IntrinsicState, so we manually recompute our dir-related event states
+ // here and send the relevant update notifications.
+ EventStates dirStates;
if (aValue && aValue->Type() == nsAttrValue::eEnum) {
SetHasValidDir();
+ dirStates |= NS_EVENT_STATE_HAS_DIR_ATTR;
Directionality dirValue = (Directionality)aValue->GetEnumValue();
if (dirValue == eDir_Auto) {
SetHasDirAuto();
+ dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO;
ClearHasFixedDir();
} else {
dir = dirValue;
SetDirectionality(dir, aNotify);
ClearHasDirAuto();
SetHasFixedDir();
+ if (dirValue == eDir_LTR) {
+ dirStates |= NS_EVENT_STATE_DIR_ATTR_LTR;
+ } else {
+ MOZ_ASSERT(dirValue == eDir_RTL);
+ dirStates |= NS_EVENT_STATE_DIR_ATTR_RTL;
+ }
}
} else {
+ if (aValue) {
+ // We have a value, just not a valid one.
+ dirStates |= NS_EVENT_STATE_HAS_DIR_ATTR;
+ }
ClearHasValidDir();
ClearHasFixedDir();
if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
SetHasDirAuto();
+ dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO;
} else {
ClearHasDirAuto();
- dir = RecomputeDirectionality(this, aNotify);
+ recomputeDirectionality = true;
}
}
+ // Now figure out what's changed about our dir states.
+ EventStates oldDirStates = State() & DIR_ATTR_STATES;
+ EventStates changedStates = dirStates ^ oldDirStates;
+ ToggleStates(changedStates, aNotify);
+ if (recomputeDirectionality) {
+ dir = RecomputeDirectionality(this, aNotify);
+ }
SetDirectionalityOnDescendants(this, dir, aNotify);
} else if (aName == nsGkAtoms::contenteditable) {
int32_t editableCountDelta = 0;
if (aOldValue &&
(aOldValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) ||
aOldValue->Equals(EmptyString(), eIgnoreCase))) {
editableCountDelta = -1;
}