Bug 1373798 part 2. Introduce event state flags that track the state of an element's "dir" attribute. r=mystor draft
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 19 Jun 2017 14:41:58 -0400
changeset 596742 b2e588d6aab951eeb46b5b1e9321c82c83e0afa4
parent 596741 c71739095e4b65e7db10f6174a875891f97738d6
child 596743 2771f8fb377b051b8dc8d93d08a2360817331f21
push id64748
push userbzbarsky@mozilla.com
push dateMon, 19 Jun 2017 18:45:34 +0000
reviewersmystor
bugs1373798
milestone56.0a1
Bug 1373798 part 2. Introduce event state flags that track the state of an element's "dir" attribute. r=mystor MozReview-Commit-ID: EDCV2fUWGmX
dom/base/Element.h
dom/events/EventStates.h
dom/html/HTMLUnknownElement.h
dom/html/nsGenericHTMLElement.cpp
--- 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;
       }