summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2020-04-17 05:50:47 -0400
committerMatt A. Tobin <email@mattatobin.com>2020-04-17 05:50:47 -0400
commit4d1d777e706322cb9aca8ed2d5a6e50b805d3bd1 (patch)
tree1cea0ad854fb25e536caf93f240258eebfb3aea8
parent32e8155127126c187ce32f7368742057bcaf69da (diff)
downloadUXP-4d1d777e706322cb9aca8ed2d5a6e50b805d3bd1.tar
UXP-4d1d777e706322cb9aca8ed2d5a6e50b805d3bd1.tar.gz
UXP-4d1d777e706322cb9aca8ed2d5a6e50b805d3bd1.tar.lz
UXP-4d1d777e706322cb9aca8ed2d5a6e50b805d3bd1.tar.xz
UXP-4d1d777e706322cb9aca8ed2d5a6e50b805d3bd1.zip
Bug 1373798 - Move HTML dir attribute state into event state flags
* Stop calling SetHasDirAuto/ClearHasDirAuto in input element code * Introduce event state flags that track the state of an element's dir attribute * Rewrite our existing checks for the state of the dir attr on top of the new event state flags * Add pseudo-classes for matching on the dir attribute states * Use the new dir attribute pseudoclasses in html.css Tag #1375
-rw-r--r--dom/base/DirectionalityUtils.cpp4
-rw-r--r--dom/base/Element.h36
-rw-r--r--dom/base/nsIContentInlines.h6
-rw-r--r--dom/base/nsINode.h28
-rw-r--r--dom/events/EventStates.h36
-rw-r--r--dom/html/HTMLInputElement.cpp27
-rw-r--r--dom/html/HTMLInputElement.h2
-rw-r--r--dom/html/HTMLUnknownElement.h3
-rw-r--r--dom/html/nsGenericHTMLElement.cpp37
-rw-r--r--layout/style/nsCSSPseudoClassList.h13
-rw-r--r--layout/style/res/html.css19
11 files changed, 139 insertions, 72 deletions
diff --git a/dom/base/DirectionalityUtils.cpp b/dom/base/DirectionalityUtils.cpp
index d9a6c4524..632abb2c8 100644
--- a/dom/base/DirectionalityUtils.cpp
+++ b/dom/base/DirectionalityUtils.cpp
@@ -728,7 +728,7 @@ WalkDescendantsResetAutoDirection(Element* aElement)
{
nsIContent* child = aElement->GetFirstChild();
while (child) {
- if (child->HasDirAuto()) {
+ if (child->IsElement() && child->AsElement()->HasDirAuto()) {
child = child->GetNextNonChildNode(aElement);
continue;
}
@@ -791,7 +791,7 @@ WalkDescendantsClearAncestorDirAuto(Element* aElement)
{
nsIContent* child = aElement->GetFirstChild();
while (child) {
- if (child->HasDirAuto()) {
+ if (child->IsElement() && child->AsElement()->HasDirAuto()) {
child = child->GetNextNonChildNode(aElement);
continue;
}
diff --git a/dom/base/Element.h b/dom/base/Element.h
index df0dbcc45..76f0767e6 100644
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -256,6 +256,23 @@ public:
void ClearStyleStateLocks();
/**
+ * Accessors for the state of our dir attribute.
+ */
+ bool HasDirAuto() const
+ {
+ return State().HasState(NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO);
+ }
+
+ /**
+ * Elements with dir="rtl" or dir="ltr".
+ */
+ bool HasFixedDir() const
+ {
+ return State().HasAtLeastOneOfStates(NS_EVENT_STATE_DIR_ATTR_LTR |
+ NS_EVENT_STATE_DIR_ATTR_RTL);
+ }
+
+ /**
* Get the inline style declaration, if any, for this element.
*/
virtual DeclarationBlock* GetInlineStyleDeclaration();
@@ -380,15 +397,6 @@ public:
bool GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult);
- // The bdi element defaults to dir=auto if it has no dir attribute set.
- // Other elements will only have dir=auto if they have an explicit dir=auto,
- // which will mean that HasValidDir() returns true but HasFixedDir() returns
- // false
- inline bool HasDirAuto() const {
- return (!HasFixedDir() &&
- (HasValidDir() || IsHTMLElement(nsGkAtoms::bdi)));
- }
-
Directionality GetComputedDirectionality() const;
inline Element* GetFlattenedTreeParentElementForStyle() const;
@@ -501,6 +509,16 @@ protected:
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:
virtual void UpdateEditableState(bool aNotify) override;
diff --git a/dom/base/nsIContentInlines.h b/dom/base/nsIContentInlines.h
index 6a82f7f65..6a9bd7afa 100644
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -87,4 +87,10 @@ nsINode::GetFlattenedTreeParentNodeForStyle() const
return ::GetFlattenedTreeParentNode<nsIContent::eForStyle>(this);
}
+inline bool
+nsINode::NodeOrAncestorHasDirAuto() const
+{
+ return AncestorHasDirAuto() || (IsElement() && AsElement()->HasDirAuto());
+}
+
#endif // nsIContentInlines_h
diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h
index 0f882445f..7c3eb9134 100644
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -1608,10 +1608,11 @@ private:
NodeIsContent,
// Set if the node has animations or transitions
ElementHasAnimations,
- // Set if node has a dir attribute with a valid value (ltr, rtl, or auto)
+ // Set if node has a dir attribute with a valid value (ltr, rtl, or auto).
+ // Note that we cannot compute this from the dir attribute event state
+ // flags, because we can't use those to distinguish
+ // <bdi dir="some-invalid-value"> and <bdi dir="auto">.
NodeHasValidDirAttribute,
- // Set if node has a dir attribute with a fixed value (ltr or rtl, NOT auto)
- NodeHasFixedDir,
// Set if the node has dir=auto and has a property pointing to the text
// node that determines its direction
NodeHasDirAutoSet,
@@ -1619,8 +1620,6 @@ private:
// and has a TextNodeDirectionalityMap property listing the elements whose
// direction it determines.
NodeHasTextNodeDirectionalityMap,
- // Set if the node has dir=auto.
- 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
@@ -1715,17 +1714,6 @@ public:
void SetHasValidDir() { SetBoolFlag(NodeHasValidDirAttribute); }
void ClearHasValidDir() { ClearBoolFlag(NodeHasValidDirAttribute); }
bool HasValidDir() const { return GetBoolFlag(NodeHasValidDirAttribute); }
- void SetHasFixedDir() {
- MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
- "SetHasFixedDir on text node");
- SetBoolFlag(NodeHasFixedDir);
- }
- void ClearHasFixedDir() {
- MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
- "ClearHasFixedDir on text node");
- ClearBoolFlag(NodeHasFixedDir);
- }
- bool HasFixedDir() const { return GetBoolFlag(NodeHasFixedDir); }
void SetHasDirAutoSet() {
MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE,
"SetHasDirAutoSet on text node");
@@ -1754,16 +1742,12 @@ public:
return GetBoolFlag(NodeHasTextNodeDirectionalityMap);
}
- void SetHasDirAuto() { SetBoolFlag(NodeHasDirAuto); }
- void ClearHasDirAuto() { ClearBoolFlag(NodeHasDirAuto); }
- bool HasDirAuto() const { return GetBoolFlag(NodeHasDirAuto); }
-
void SetAncestorHasDirAuto() { SetBoolFlag(NodeAncestorHasDirAuto); }
void ClearAncestorHasDirAuto() { ClearBoolFlag(NodeAncestorHasDirAuto); }
bool AncestorHasDirAuto() const { return GetBoolFlag(NodeAncestorHasDirAuto); }
- bool NodeOrAncestorHasDirAuto() const
- { return HasDirAuto() || AncestorHasDirAuto(); }
+ // Implemented in nsIContentInlines.h.
+ inline bool NodeOrAncestorHasDirAuto() const;
void SetIsElementInStyleScope(bool aValue) {
MOZ_ASSERT(IsElement(), "SetIsInStyleScope on a non-Element node");
diff --git a/dom/events/EventStates.h b/dom/events/EventStates.h
index 2672d2897..bda36c040 100644
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -292,14 +292,40 @@ private:
#define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(43)
// Element is highlighted (devtools inspector)
#define NS_EVENT_STATE_DEVTOOLS_HIGHLIGHTED 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 an unresolved custom element candidate
-#define NS_EVENT_STATE_UNRESOLVED NS_DEFINE_EVENT_STATE_MACRO(46)
+#define NS_EVENT_STATE_UNRESOLVED NS_DEFINE_EVENT_STATE_MACRO(50)
// Element is transitioning for rules changed by style editor
-#define NS_EVENT_STATE_STYLEEDITOR_TRANSITIONING NS_DEFINE_EVENT_STATE_MACRO(47)
+#define NS_EVENT_STATE_STYLEEDITOR_TRANSITIONING NS_DEFINE_EVENT_STATE_MACRO(51)
// Content shows its placeholder
-#define NS_EVENT_STATE_PLACEHOLDERSHOWN NS_DEFINE_EVENT_STATE_MACRO(48)
+#define NS_EVENT_STATE_PLACEHOLDERSHOWN NS_DEFINE_EVENT_STATE_MACRO(52)
// Element has focus-within.
-#define NS_EVENT_STATE_FOCUS_WITHIN NS_DEFINE_EVENT_STATE_MACRO(49)
+#define NS_EVENT_STATE_FOCUS_WITHIN NS_DEFINE_EVENT_STATE_MACRO(53)
+
+#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 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)
@@ -310,7 +336,7 @@ private:
#define DIRECTION_STATES (NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL)
-#define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \
+#define ESM_MANAGED_STATES (DIR_ATTR_STATES | NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS | \
NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER | \
NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_UNRESOLVED | \
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp
index 7c2688b7e..7708a60ac 100644
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1264,10 +1264,6 @@ HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
}
} else if (aNotify && aName == nsGkAtoms::disabled) {
mDisabledChanged = true;
- } else if (aName == nsGkAtoms::dir &&
- AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
- nsGkAtoms::_auto, eIgnoreCase)) {
- SetDirectionIfAuto(false, aNotify);
} else if (mType == NS_FORM_INPUT_RADIO && aName == nsGkAtoms::required) {
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
@@ -1419,7 +1415,7 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
"HTML5 spec does not allow underflow for type=range");
} else if (aName == nsGkAtoms::dir &&
aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
- SetDirectionIfAuto(true, aNotify);
+ SetDirectionFromValue(aNotify);
} else if (aName == nsGkAtoms::lang) {
if (mType == NS_FORM_INPUT_NUMBER) {
// Update the value that is displayed to the user to the new locale:
@@ -4975,7 +4971,9 @@ HTMLInputElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
}
// Set direction based on value if dir=auto
- SetDirectionIfAuto(HasDirAuto(), false);
+ if (HasDirAuto()) {
+ SetDirectionFromValue(false);
+ }
// An element can't suffer from value missing if it is not in a document.
// We have to check if we suffer from that as we are now in a document.
@@ -6693,17 +6691,12 @@ HTMLInputElement::SetDefaultValueAsValue()
}
void
-HTMLInputElement::SetDirectionIfAuto(bool aAuto, bool aNotify)
+HTMLInputElement::SetDirectionFromValue(bool aNotify)
{
- if (aAuto) {
- SetHasDirAuto();
- if (IsSingleLineTextControl(true)) {
- nsAutoString value;
- GetValue(value);
- SetDirectionalityFromValue(this, value, aNotify);
- }
- } else {
- ClearHasDirAuto();
+ if (IsSingleLineTextControl(true)) {
+ nsAutoString value;
+ GetValue(value);
+ SetDirectionalityFromValue(this, value, aNotify);
}
}
@@ -8480,7 +8473,7 @@ HTMLInputElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
UpdateAllValidityStates(aNotify);
if (HasDirAuto()) {
- SetDirectionIfAuto(true, aNotify);
+ SetDirectionFromValue(aNotify);
}
// :placeholder-shown pseudo-class may change when the value changes.
diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h
index 48807f733..55bb59ec9 100644
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -1122,7 +1122,7 @@ protected:
*/
nsresult SetDefaultValueAsValue();
- virtual void SetDirectionIfAuto(bool aAuto, bool aNotify);
+ void SetDirectionFromValue(bool aNotify);
/**
* Return if an element should have a specific validity UI
diff --git a/dom/html/HTMLUnknownElement.h b/dom/html/HTMLUnknownElement.h
index c77fba919..6390cc576 100644
--- a/dom/html/HTMLUnknownElement.h
+++ b/dom/html/HTMLUnknownElement.h
@@ -7,6 +7,7 @@
#define mozilla_dom_HTMLUnknownElement_h
#include "mozilla/Attributes.h"
+#include "mozilla/EventStates.h"
#include "nsGenericHTMLElement.h"
namespace mozilla {
@@ -27,7 +28,7 @@ public:
: nsGenericHTMLElement(aNodeInfo)
{
if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
- SetHasDirAuto();
+ AddStatesSilently(NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO);
}
}
diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp
index be78dc1cf..3cf19ea8f 100644
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -708,28 +708,49 @@ nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
}
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();
- ClearHasFixedDir();
+ dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO;
} 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;
diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h
index 701578338..6d5b6eca1 100644
--- a/layout/style/nsCSSPseudoClassList.h
+++ b/layout/style/nsCSSPseudoClassList.h
@@ -211,6 +211,19 @@ CSS_STATE_PSEUDO_CLASS(mozMathIncrementScriptLevel,
":-moz-math-increment-script-level", 0, "",
NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL)
+CSS_STATE_PSEUDO_CLASS(mozHasDirAttr, ":-moz-has-dir-attr",
+ CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "",
+ NS_EVENT_STATE_HAS_DIR_ATTR)
+CSS_STATE_PSEUDO_CLASS(mozDirAttrLTR, ":-moz-dir-attr-ltr",
+ CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "",
+ NS_EVENT_STATE_DIR_ATTR_LTR)
+CSS_STATE_PSEUDO_CLASS(mozDirAttrRTL, ":-moz-dir-attr-rtl",
+ CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "",
+ NS_EVENT_STATE_DIR_ATTR_RTL)
+CSS_STATE_PSEUDO_CLASS(mozDirAttrLikeAuto, ":-moz-dir-attr-like-auto",
+ CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "",
+ NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO)
+
// CSS 3 UI
// http://www.w3.org/TR/2004/CR-css3-ui-20040511/#pseudo-classes
CSS_STATE_PSEUDO_CLASS(required, ":required", 0, "", NS_EVENT_STATE_REQUIRED)
diff --git a/layout/style/res/html.css b/layout/style/res/html.css
index 1f572467f..ea8efbe24 100644
--- a/layout/style/res/html.css
+++ b/layout/style/res/html.css
@@ -7,18 +7,18 @@
/* bidi */
-[dir] {
+:-moz-has-dir-attr {
unicode-bidi: isolate;
}
-[dir="rtl"] {
+:-moz-dir-attr-rtl {
direction: rtl;
}
-[dir="ltr"] {
+:-moz-dir-attr-ltr {
direction: ltr;
}
-bdi:dir(ltr), [dir="auto"]:dir(ltr) { direction: ltr; }
-bdi:dir(rtl), [dir="auto"]:dir(rtl) { direction: rtl; }
+:-moz-dir-attr-like-auto:dir(ltr) { direction: ltr; }
+:-moz-dir-attr-like-auto:dir(rtl) { direction: rtl; }
/* To ensure http://www.w3.org/TR/REC-html40/struct/dirlang.html#style-bidi:
*
@@ -89,10 +89,15 @@ xmp {
bdi, output {
unicode-bidi: isolate;
}
-bdo, bdo[dir] {
+/* We need the "bdo:-moz-has-dir-attr" bit because "bdo" has lower
+ specificity than the ":-moz-has-dir-attr" selector above. */
+bdo, bdo:-moz-has-dir-attr {
unicode-bidi: isolate-override;
}
-textarea[dir="auto"], pre[dir="auto"] { unicode-bidi: plaintext; }
+textarea:-moz-dir-attr-like-auto,
+pre:-moz-dir-attr-like-auto {
+ unicode-bidi: plaintext;
+}
/* blocks */