Bug 1053898 - Update Walker and Node actors to listen to slotchange events on shadow roots;r=bgrins
MozReview-Commit-ID: 9LfiR7EFq3I
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -1018,17 +1018,18 @@ MarkupView.prototype = {
// Container might not exist if this came from a load event for a node
// we're not viewing.
continue;
}
if (type === "attributes" || type === "characterData"
|| type === "events" || type === "pseudoClassLock") {
container.update();
- } else if (type === "childList" || type === "nativeAnonymousChildList") {
+ } else if (type === "childList" || type === "nativeAnonymousChildList"
+ || type === "slotchange") {
container.childrenDirty = true;
// Update the children to take care of changes in the markup view DOM
// and update container (and its subtree) DOM tree depth level for
// accessibility where necessary.
this._updateChildren(container, {flash: true}).then(() =>
container.updateLevel());
} else if (type === "inlineTextChild") {
container.childrenDirty = true;
--- a/devtools/server/actors/inspector/node.js
+++ b/devtools/server/actors/inspector/node.js
@@ -72,16 +72,24 @@ const NodeActor = protocol.ActorClassWit
protocol.Actor.prototype.destroy.call(this);
if (this.mutationObserver) {
if (!Cu.isDeadWrapper(this.mutationObserver)) {
this.mutationObserver.disconnect();
}
this.mutationObserver = null;
}
+
+ if (this.slotchangeListener) {
+ if (!InspectorActorUtils.isNodeDead(this)) {
+ this.rawNode.removeEventListener("slotchange", this.slotchangeListener);
+ }
+ this.slotchangeListener = null;
+ }
+
this.rawNode = null;
this.walker = null;
},
// Returns the JSON representation of this object over the wire.
form: function(detail) {
if (detail === "actorid") {
return this.actorID;
@@ -163,16 +171,24 @@ const NodeActor = protocol.ActorClassWit
characterData: true,
characterDataOldValue: true,
childList: true,
subtree: true
});
this.mutationObserver = observer;
},
+ /**
+ * Watch for all "slotchange" events on the node.
+ */
+ watchSlotchange: function(callback) {
+ this.slotchangeListener = callback;
+ this.rawNode.addEventListener("slotchange", this.slotchangeListener);
+ },
+
get isBeforePseudoElement() {
return this.rawNode.nodeName === "_moz_generated_content_before";
},
get isAfterPseudoElement() {
return this.rawNode.nodeName === "_moz_generated_content_after";
},
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -134,16 +134,17 @@ var WalkerActor = protocol.ActorClassWit
this._orphaned = new Set();
// The client can tell the walker that it is interested in a node
// even when it is orphaned with the `retainNode` method. This
// list contains orphaned nodes that were so retained.
this._retainedOrphans = new Set();
this.onMutations = this.onMutations.bind(this);
+ this.onSlotchange = this.onSlotchange.bind(this);
this.onFrameLoad = this.onFrameLoad.bind(this);
this.onFrameUnload = this.onFrameUnload.bind(this);
this._throttledEmitNewMutations = throttle(this._emitNewMutations.bind(this),
MUTATIONS_THROTTLING_DELAY);
tabActor.on("will-navigate", this.onFrameUnload);
tabActor.on("window-ready", this.onFrameLoad);
@@ -303,16 +304,21 @@ var WalkerActor = protocol.ActorClassWit
// Add the node actor as a child of this walker actor, assigning
// it an actorID.
this.manage(actor);
this._refMap.set(node, actor);
if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
actor.watchDocument(this.onMutations);
}
+
+ if (actor.isShadowRoot) {
+ actor.watchSlotchange(this.onSlotchange);
+ }
+
return actor;
},
_onReflows: function(reflows) {
// Going through the nodes the walker knows about, see which ones have
// had their display changed and send a display-change event if any
let changes = [];
for (let [node, actor] of this._refMap) {
@@ -1642,16 +1648,29 @@ var WalkerActor = protocol.ActorClassWit
this.queueMutation({
type: "inlineTextChild",
target: parentActor.actorID,
inlineTextChild:
inlineTextChild ? inlineTextChild.form() : undefined
});
},
+ onSlotchange: function(event) {
+ let target = event.target;
+ let targetActor = this.getNode(target);
+ if (!targetActor) {
+ return;
+ }
+
+ this.queueMutation({
+ type: "slotchange",
+ target: targetActor.actorID
+ });
+ },
+
onFrameLoad: function({ window, isTopLevel }) {
let { readyState } = window.document;
if (readyState != "interactive" && readyState != "complete") {
window.addEventListener("DOMContentLoaded",
this.onFrameLoad.bind(this, { window, isTopLevel }),
{ once: true });
return;
}