Bug 1053898 - Update Walker and Node actors to listen to slotchange events on shadow roots;r=bgrins
authorJulian Descottes <jdescottes@mozilla.com>
Mon, 05 Mar 2018 18:24:53 +0100
changeset 410632 cc810ea58733d950e1a008666c169337786bdc1a
parent 410631 78525c932721915a7f5e93e34696fe4bf7ef6dab
child 410633 f6ced0c49bff14b4758166463ba477f92c6eb99e
push id33733
push useraciure@mozilla.com
push dateThu, 29 Mar 2018 22:05:29 +0000
treeherdermozilla-central@7ca58ce09779 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbgrins
bugs1053898
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1053898 - Update Walker and Node actors to listen to slotchange events on shadow roots;r=bgrins MozReview-Commit-ID: 9LfiR7EFq3I
devtools/client/inspector/markup/markup.js
devtools/server/actors/inspector/node.js
devtools/server/actors/inspector/walker.js
--- 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;
     }