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
--- 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