Bug 1323618 - Allow locking off of psuedo-classes through inIDOMUtils. r?heycam draft
authorJared Wein <jwein@mozilla.com>
Thu, 15 Dec 2016 00:49:44 -0500
changeset 449821 15dea178f722db887a189a3842933add23f166cd
parent 449666 18b5a7a5d833f09b1f1e5cc45f4c0ff6235a1d5e
child 539594 135accae276b6a7ab5c269ae437999a0dade5173
push id38680
push userbmo:jaws@mozilla.com
push dateThu, 15 Dec 2016 05:50:29 +0000
reviewersheycam
bugs1323618
milestone53.0a1
Bug 1323618 - Allow locking off of psuedo-classes through inIDOMUtils. r?heycam This patch doesn't currently work and I believe it is due to how I implemented EventStates::SetEnabled, though I couldn't get my debugger to break at Element.h's StyleState to confirm that StyleStateFromLocks was getting the expected data. I created a parallel mStatesEnabled bitset. This bitset is used in conjunction with mStates. If a bit is set to true in mStates and set to false in mStatesEnabled, then it should be 'locked' to off. If a bit is true in mStates and is true in mStatesEnabled, then it should be 'locked' to on. I think we could do this without using twice the memory, but I couldn't think of a way to keep track of which states should be disabled. MozReview-Commit-ID: DppYTmILpwy * * * [mq]: temp MozReview-Commit-ID: 74iIOQumfrw
dom/base/Element.cpp
dom/base/Element.h
dom/events/EventStates.h
layout/inspector/inDOMUtils.cpp
layout/inspector/inIDOMUtils.idl
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -351,16 +351,21 @@ Element::StyleStateFromLocks() const
   EventStates state = mState | locks;
 
   if (locks.HasState(NS_EVENT_STATE_VISITED)) {
     return state & ~NS_EVENT_STATE_UNVISITED;
   }
   if (locks.HasState(NS_EVENT_STATE_UNVISITED)) {
     return state & ~NS_EVENT_STATE_VISITED;
   }
+
+  if (locks.HasDisabledStates()) {
+    return state & locks.GetEnabledStates();
+  }
+
   return state;
 }
 
 EventStates
 Element::LockedStyleStates() const
 {
   EventStates* locks =
     static_cast<EventStates*>(GetProperty(nsGkAtoms::lockedStyleStates));
@@ -379,20 +384,22 @@ Element::NotifyStyleStateChange(EventSta
     if (presShell) {
       nsAutoScriptBlocker scriptBlocker;
       presShell->ContentStateChanged(doc, this, aStates);
     }
   }
 }
 
 void
-Element::LockStyleStates(EventStates aStates)
+Element::LockStyleStates(EventStates aStates, bool aEnabled)
 {
   EventStates* locks = new EventStates(LockedStyleStates());
 
+  aStates.SetEnabled(aEnabled);
+
   *locks |= aStates;
 
   if (aStates.HasState(NS_EVENT_STATE_VISITED)) {
     *locks &= ~NS_EVENT_STATE_UNVISITED;
   }
   if (aStates.HasState(NS_EVENT_STATE_UNVISITED)) {
     *locks &= ~NS_EVENT_STATE_VISITED;
   }
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -246,18 +246,19 @@ public:
 
   /**
    * The style state locks applied to this element.
    */
   EventStates LockedStyleStates() const;
 
   /**
    * Add a style state lock on this element.
+   * If aEnabled = false, the negation of aStates is locked.
    */
-  void LockStyleStates(EventStates aStates);
+  void LockStyleStates(EventStates aStates, bool aEnabled);
 
   /**
    * Remove a style state lock on this element.
    */
   void UnlockStyleStates(EventStates aStates);
 
   /**
    * Clear all style state locks on this element.
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -24,51 +24,55 @@ namespace mozilla {
 class EventStates
 {
 public:
   typedef uint64_t InternalType;
   typedef uint8_t ServoType;
 
   constexpr EventStates()
     : mStates(0)
+    , mStatesEnabled(0)
   {
   }
 
   // NOTE: the ideal scenario would be to have the default constructor public
   // setting mStates to 0 and this constructor (without = 0) private.
   // In that case, we could be sure that only macros at the end were creating
   // EventStates instances with mStates set to something else than 0.
   // Unfortunately, this constructor is needed at at least two places now.
   explicit constexpr EventStates(InternalType aStates)
     : mStates(aStates)
+    , mStatesEnabled(aStates)
   {
   }
 
   EventStates constexpr operator|(const EventStates& aEventStates) const
   {
     return EventStates(mStates | aEventStates.mStates);
   }
 
   EventStates& operator|=(const EventStates& aEventStates)
   {
     mStates |= aEventStates.mStates;
+    mStatesEnabled |= aEventStates.mStatesEnabled;
     return *this;
   }
 
   // NOTE: calling if (eventStates1 & eventStates2) will not build.
   // This might work correctly if operator bool() is defined
   // but using HasState, HasAllStates or HasAtLeastOneOfStates is recommended.
   EventStates constexpr operator&(const EventStates& aEventStates) const
   {
     return EventStates(mStates & aEventStates.mStates);
   }
 
   EventStates& operator&=(const EventStates& aEventStates)
   {
     mStates &= aEventStates.mStates;
+    mStatesEnabled &= aEventStates.mStatesEnabled;
     return *this;
   }
 
   bool operator==(const EventStates& aEventStates) const
   {
     return mStates == aEventStates.mStates;
   }
 
@@ -85,28 +89,29 @@ public:
   EventStates operator^(const EventStates& aEventStates) const
   {
     return EventStates(mStates ^ aEventStates.mStates);
   }
 
   EventStates& operator^=(const EventStates& aEventStates)
   {
     mStates ^= aEventStates.mStates;
+    mStatesEnabled ^= aEventStates.mStatesEnabled;
     return *this;
   }
 
   /**
    * Returns true if the EventStates instance is empty.
    * A EventStates instance is empty if it contains no state.
    *
    * @return Whether if the object is empty.
    */
   bool IsEmpty() const
   {
-    return mStates == 0;
+    return mStates == 0 && mStatesEnabled == 0;
   }
 
   /**
    * Returns true if the EventStates instance contains the state
    * contained in aEventStates.
    * @note aEventStates should contain only one state.
    *
    * @param aEventStates The state to check.
@@ -147,32 +152,51 @@ public:
    *
    * @return Whether the object has all states from aEventStates
    */
   bool HasAllStates(EventStates aEventStates) const
   {
     return (mStates & aEventStates.mStates) == aEventStates.mStates;
   }
 
+  void SetEnabled(bool aEnabled)
+  {
+    mStatesEnabled |= mStates;
+    if (!aEnabled) {
+      mStatesEnabled &= ~mStatesEnabled;
+    }
+  }
+
+  bool HasDisabledStates() const
+  {
+    return mStates != mStatesEnabled;
+  }
+
+  EventStates GetEnabledStates() const
+  {
+    return EventStates(mStates & mStatesEnabled);
+  }
+
   // We only need that method for inDOMUtils::GetContentState.
   // If inDOMUtils::GetContentState is removed, this method should be removed.
   InternalType GetInternalValue() const {
     return mStates;
   }
 
   /**
    * Method used to get the appropriate state representation for Servo.
    */
   ServoType ServoValue() const
   {
     return mStates & ((1 << (NS_EVENT_STATE_HIGHEST_SERVO_BIT + 1)) - 1);
   }
 
 private:
   InternalType mStates;
+  InternalType mStatesEnabled;
 };
 
 } // namespace mozilla
 
 /**
  * The following macros are creating EventStates instance with different
  * values depending of their meaning.
  * Ideally, EventStates instance with values different than 0 should only be
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -1249,27 +1249,29 @@ inDOMUtils::GetCSSPseudoElementNames(uin
     ret[i] = ToNewUnicode(nsDependentAtomString(array[i]));
   }
   *aNames = ret;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 inDOMUtils::AddPseudoClassLock(nsIDOMElement *aElement,
-                               const nsAString &aPseudoClass)
+                               const nsAString &aPseudoClass,
+                               bool aEnabled,
+                               uint8_t aArgc)
 {
   EventStates state = GetStatesForPseudoClass(aPseudoClass);
   if (state.IsEmpty()) {
     return NS_OK;
   }
 
   nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement);
   NS_ENSURE_ARG_POINTER(element);
 
-  element->LockStyleStates(state);
+  element->LockStyleStates(state, aArgc > 0 ? aEnabled : true);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 inDOMUtils::RemovePseudoClassLock(nsIDOMElement *aElement,
                                   const nsAString &aPseudoClass)
 {
--- a/layout/inspector/inIDOMUtils.idl
+++ b/layout/inspector/inIDOMUtils.idl
@@ -181,17 +181,19 @@ interface inIDOMUtils : nsISupports
    * @param {wstring[]} aNames the names
    */
   void getCSSPseudoElementNames([optional] out unsigned long aCount,
                                 [retval, array, size_is(aCount)] out wstring aNames);
 
   // pseudo-class style locking methods. aPseudoClass must be a valid pseudo-class
   // selector string, e.g. ":hover". ":any-link" and non-event-state
   // pseudo-classes are ignored.
-  void addPseudoClassLock(in nsIDOMElement aElement, in DOMString aPseudoClass);
+  [optional_argc] void addPseudoClassLock(in nsIDOMElement aElement,
+                          in DOMString aPseudoClass,
+                          [optional] in boolean aEnabled);
   void removePseudoClassLock(in nsIDOMElement aElement, in DOMString aPseudoClass);
   bool hasPseudoClassLock(in nsIDOMElement aElement, in DOMString aPseudoClass);
   void clearPseudoClassLocks(in nsIDOMElement aElement);
 
   /**
    * Parse CSS and update the style sheet in place.
    *
    * @param DOMCSSStyleSheet aSheet