From 072c1a6ba355a03295df16a7f6a8ffef5075c0e7 Mon Sep 17 00:00:00 2001 From: Jared Wein Date: Thu, 12 Jan 2017 12:10:07 -0500 Subject: [PATCH] Bug 1323618 - Allow locking off of psuedo-classes through inIDOMUtils. r=heycam MozReview-Commit-ID: DppYTmILpwy --HG-- extra : rebase_source : c0befc8f86b92cfc88b537e520c3aa032846ba6a --- devtools/server/actors/inspector.js | 12 +++-- .../mochitest/inspector-traversal-data.html | 1 + .../test_inspector-pseudoclass-lock.html | 25 ++++++++-- devtools/shared/specs/inspector.js | 3 +- dom/base/Element.cpp | 48 +++++++++++-------- dom/base/Element.h | 16 ++++++- layout/inspector/inDOMUtils.cpp | 8 ++-- layout/inspector/inIDOMUtils.idl | 7 ++- 8 files changed, 84 insertions(+), 36 deletions(-) diff --git a/devtools/server/actors/inspector.js b/devtools/server/actors/inspector.js index b5586e84d48c..41f873ea7f6b 100644 --- a/devtools/server/actors/inspector.js +++ b/devtools/server/actors/inspector.js @@ -1755,6 +1755,8 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, { * Options object: * `parents`: True if the pseudo-class should be added * to parent nodes. + * `enabled`: False if the pseudo-class should be locked + * to 'off'. Defaults to true. * * @returns An empty packet. A "pseudoClassLock" mutation will * be queued for any changed nodes. @@ -1772,7 +1774,9 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, { } } - this._addPseudoClassLock(node, pseudo); + let enabled = options.enabled === undefined || + options.enabled; + this._addPseudoClassLock(node, pseudo, enabled); if (!options.parents) { return; @@ -1782,7 +1786,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, { let cur; while ((cur = walker.parentNode())) { let curNode = this._ref(cur); - this._addPseudoClassLock(curNode, pseudo); + this._addPseudoClassLock(curNode, pseudo, enabled); } }, @@ -1794,11 +1798,11 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, { }); }, - _addPseudoClassLock: function (node, pseudo) { + _addPseudoClassLock: function (node, pseudo, enabled) { if (node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) { return false; } - DOMUtils.addPseudoClassLock(node.rawNode, pseudo); + DOMUtils.addPseudoClassLock(node.rawNode, pseudo, enabled); this._activePseudoClassLocks.add(node); this._queuePseudoClassMutation(node); return true; diff --git a/devtools/server/tests/mochitest/inspector-traversal-data.html b/devtools/server/tests/mochitest/inspector-traversal-data.html index 45b8c2ededc4..840b444a5314 100644 --- a/devtools/server/tests/mochitest/inspector-traversal-data.html +++ b/devtools/server/tests/mochitest/inspector-traversal-data.html @@ -1,3 +1,4 @@ + diff --git a/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html b/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html index 64bb03f80a57..821a4ab7328d 100644 --- a/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html +++ b/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html @@ -60,16 +60,21 @@ function checkChange(change, expectation) { is(target.pseudoClassLocks.length, expectation.pseudos.length, "Expect " + expectation.pseudos.length + " pseudoclass locks."); - for (let pseudo of expectation.pseudos) { + for (let i = 0; i < expectation.pseudos.length; i++) { + let pseudo = expectation.pseudos[i]; + let enabled = expectation.enabled === undefined ? true : expectation.enabled[i]; ok(target.hasPseudoClassLock(pseudo), "Expect lock: " + pseudo); - ok(DOMUtils.hasPseudoClassLock(target.rawNode(), pseudo), "Expect lock in dom: " + pseudo); + let rawNode = target.rawNode(); + ok(DOMUtils.hasPseudoClassLock(rawNode, pseudo), "Expect lock in dom: " + pseudo); + + is(rawNode.matches(pseudo), enabled, + `Target should match pseudoclass, '${pseudo}', if enabled (with .matches())`); } for (let pseudo of KNOWN_PSEUDOCLASSES) { if (!expectation.pseudos.some(expected => pseudo === expected)) { ok(!target.hasPseudoClassLock(pseudo), "Don't expect lock: " + pseudo); ok(!DOMUtils.hasPseudoClassLock(target.rawNode(), pseudo), "Don't expect lock in dom: " + pseudo); - } } } @@ -93,7 +98,7 @@ addTest(function testPseudoClassLock() { // Expect a single pseudoClassLock mutation. return promiseOnce(gWalker, "mutations"); }).then(mutations => { - is(mutations.length, 1, "Should get one mutations"); + is(mutations.length, 1, "Should get one mutation"); is(mutations[0].target, nodeFront, "Should be the node we tried to apply to"); checkChange(mutations[0], { id: "b", @@ -149,6 +154,18 @@ addTest(function testPseudoClassLock() { pseudos: [] }]; checkMutations(mutations, expectedMutations); + }).then(() => { + gWalker.addPseudoClassLock(nodeFront, ":hover", {enabled: false}); + return promiseOnce(gWalker, "mutations"); + }).then(mutations => { + is(mutations.length, 1, "Should get one mutation"); + is(mutations[0].target, nodeFront, "Should be the node we tried to apply to"); + checkChange(mutations[0], { + id: "b", + nodeName: "DIV", + pseudos: [":hover", ":active"], + enabled: [false, true] + }); }).then(() => { // Now shut down the walker and make sure that clears up the remaining lock. return gWalker.release(); diff --git a/devtools/shared/specs/inspector.js b/devtools/shared/specs/inspector.js index e07ec67c3765..5a087379530e 100644 --- a/devtools/shared/specs/inspector.js +++ b/devtools/shared/specs/inspector.js @@ -234,7 +234,8 @@ const walkerSpec = generateActorSpec({ request: { node: Arg(0, "domnode"), pseudoClass: Arg(1), - parents: Option(2) + parents: Option(2), + enabled: Option(2, "boolean"), }, response: {} }, diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index adf22ee89857..3b91fded304b 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -346,27 +346,30 @@ Element::Blur(mozilla::ErrorResult& aError) EventStates Element::StyleStateFromLocks() const { - EventStates locks = LockedStyleStates(); - EventStates state = mState | locks; + StyleStateLocks locksAndValues = LockedStyleStates(); + EventStates locks = locksAndValues.mLocks; + EventStates values = locksAndValues.mValues; + EventStates state = (mState & ~locks) | (locks & values); - if (locks.HasState(NS_EVENT_STATE_VISITED)) { + if (state.HasState(NS_EVENT_STATE_VISITED)) { return state & ~NS_EVENT_STATE_UNVISITED; } - if (locks.HasState(NS_EVENT_STATE_UNVISITED)) { + if (state.HasState(NS_EVENT_STATE_UNVISITED)) { return state & ~NS_EVENT_STATE_VISITED; } + return state; } -EventStates +Element::StyleStateLocks Element::LockedStyleStates() const { - EventStates* locks = - static_cast(GetProperty(nsGkAtoms::lockedStyleStates)); + StyleStateLocks* locks = + static_cast(GetProperty(nsGkAtoms::lockedStyleStates)); if (locks) { return *locks; } - return EventStates(); + return StyleStateLocks(); } void @@ -383,21 +386,26 @@ Element::NotifyStyleStateChange(EventStates aStates) } void -Element::LockStyleStates(EventStates aStates) +Element::LockStyleStates(EventStates aStates, bool aEnabled) { - EventStates* locks = new EventStates(LockedStyleStates()); + StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates()); - *locks |= aStates; + locks->mLocks |= aStates; + if (aEnabled) { + locks->mValues |= aStates; + } else { + locks->mValues &= ~aStates; + } if (aStates.HasState(NS_EVENT_STATE_VISITED)) { - *locks &= ~NS_EVENT_STATE_UNVISITED; + locks->mLocks &= ~NS_EVENT_STATE_UNVISITED; } if (aStates.HasState(NS_EVENT_STATE_UNVISITED)) { - *locks &= ~NS_EVENT_STATE_VISITED; + locks->mLocks &= ~NS_EVENT_STATE_VISITED; } SetProperty(nsGkAtoms::lockedStyleStates, locks, - nsINode::DeleteProperty); + nsINode::DeleteProperty); SetHasLockedStyleStates(); NotifyStyleStateChange(aStates); @@ -406,18 +414,18 @@ Element::LockStyleStates(EventStates aStates) void Element::UnlockStyleStates(EventStates aStates) { - EventStates* locks = new EventStates(LockedStyleStates()); + StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates()); - *locks &= ~aStates; + locks->mLocks &= ~aStates; - if (locks->IsEmpty()) { + if (locks->mLocks.IsEmpty()) { DeleteProperty(nsGkAtoms::lockedStyleStates); ClearHasLockedStyleStates(); delete locks; } else { SetProperty(nsGkAtoms::lockedStyleStates, locks, - nsINode::DeleteProperty); + nsINode::DeleteProperty); } NotifyStyleStateChange(aStates); @@ -426,12 +434,12 @@ Element::UnlockStyleStates(EventStates aStates) void Element::ClearStyleStateLocks() { - EventStates locks = LockedStyleStates(); + StyleStateLocks locks = LockedStyleStates(); DeleteProperty(nsGkAtoms::lockedStyleStates); ClearHasLockedStyleStates(); - NotifyStyleStateChange(locks); + NotifyStyleStateChange(locks.mLocks); } bool diff --git a/dom/base/Element.h b/dom/base/Element.h index c697fcfd1f47..a18d110c069b 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -244,15 +244,27 @@ public: return StyleStateFromLocks(); } + /** + * StyleStateLocks is used to specify which event states should be locked, + * and whether they should be locked to on or off. + */ + struct StyleStateLocks { + // mLocks tracks which event states should be locked. + EventStates mLocks; + // mValues tracks if the locked state should be on or off. + EventStates mValues; + }; + /** * The style state locks applied to this element. */ - EventStates LockedStyleStates() const; + StyleStateLocks LockedStyleStates() const; /** * Add a style state lock on this element. + * aEnabled is the value to lock the given state bits to. */ - void LockStyleStates(EventStates aStates); + void LockStyleStates(EventStates aStates, bool aEnabled); /** * Remove a style state lock on this element. diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index c1156a36c662..5e400f8d2fe3 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -1254,7 +1254,9 @@ inDOMUtils::GetCSSPseudoElementNames(uint32_t* aLength, char16_t*** aNames) NS_IMETHODIMP inDOMUtils::AddPseudoClassLock(nsIDOMElement *aElement, - const nsAString &aPseudoClass) + const nsAString &aPseudoClass, + bool aEnabled, + uint8_t aArgc) { EventStates state = GetStatesForPseudoClass(aPseudoClass); if (state.IsEmpty()) { @@ -1264,7 +1266,7 @@ inDOMUtils::AddPseudoClassLock(nsIDOMElement *aElement, nsCOMPtr element = do_QueryInterface(aElement); NS_ENSURE_ARG_POINTER(element); - element->LockStyleStates(state); + element->LockStyleStates(state, aArgc > 0 ? aEnabled : true); return NS_OK; } @@ -1300,7 +1302,7 @@ inDOMUtils::HasPseudoClassLock(nsIDOMElement *aElement, nsCOMPtr element = do_QueryInterface(aElement); NS_ENSURE_ARG_POINTER(element); - EventStates locks = element->LockedStyleStates(); + EventStates locks = element->LockedStyleStates().mLocks; *_retval = locks.HasAllStates(state); return NS_OK; diff --git a/layout/inspector/inIDOMUtils.idl b/layout/inspector/inIDOMUtils.idl index 30c15003fb52..7c36c680ed96 100644 --- a/layout/inspector/inIDOMUtils.idl +++ b/layout/inspector/inIDOMUtils.idl @@ -185,8 +185,11 @@ interface inIDOMUtils : nsISupports // 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); + // pseudo-classes are ignored. aEnabled sets whether the psuedo-class + // should be locked to on or off. + [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);