Bug 1323618 - Allow locking off of psuedo-classes through inIDOMUtils. r=heycam

MozReview-Commit-ID: DppYTmILpwy

--HG--
extra : rebase_source : c0befc8f86b92cfc88b537e520c3aa032846ba6a
This commit is contained in:
Jared Wein 2017-01-12 12:10:07 -05:00
parent 90c9daf114
commit 072c1a6ba3
8 changed files with 84 additions and 36 deletions

View File

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

View File

@ -1,3 +1,4 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">

View File

@ -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();

View File

@ -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: {}
},

View File

@ -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<EventStates*>(GetProperty(nsGkAtoms::lockedStyleStates));
StyleStateLocks* locks =
static_cast<StyleStateLocks*>(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<EventStates>);
nsINode::DeleteProperty<StyleStateLocks>);
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<EventStates>);
nsINode::DeleteProperty<StyleStateLocks>);
}
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

View File

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

View File

@ -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<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;
}
@ -1300,7 +1302,7 @@ inDOMUtils::HasPseudoClassLock(nsIDOMElement *aElement,
nsCOMPtr<mozilla::dom::Element> 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;

View File

@ -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);