Bug 1395367 - In-page hightlight the active iframe in the the iframe document selector draft
authorIan Moody <moz-ian@perix.co.uk>
Wed, 13 Sep 2017 22:10:54 +0100
changeset 664291 08a81370c487462b8b7bf4294ac7a266da4314ff
parent 663831 1888ec2f277f6bb26271b8808e08914a21db9efe
child 731421 69ea76f023f69f8e07455aa9faf82dc0793a32fe
push id79672
push usermoz-ian@perix.co.uk
push dateWed, 13 Sep 2017 21:12:05 +0000
bugs1395367
milestone57.0a1
Bug 1395367 - In-page hightlight the active iframe in the the iframe document selector MozReview-Commit-ID: 7DnW4iwGnfA
devtools/client/framework/menu-item.js
devtools/client/framework/menu.js
devtools/client/framework/toolbox.js
devtools/server/actors/inspector.js
devtools/shared/fronts/inspector.js
devtools/shared/specs/inspector.js
--- a/devtools/client/framework/menu-item.js
+++ b/devtools/client/framework/menu-item.js
@@ -40,26 +40,28 @@
  *    Boolean visible
  *      If false, the menu item will be entirely hidden.
  */
 function MenuItem({
     accesskey = null,
     checked = false,
     click = () => {},
     disabled = false,
+    DOMMenuItemActive = () => {},
+    id = null,
     label = "",
-    id = null,
     submenu = null,
     type = "normal",
     visible = true,
 } = { }) {
   this.accesskey = accesskey;
   this.checked = checked;
   this.click = click;
   this.disabled = disabled;
+  this.DOMMenuItemActive = DOMMenuItemActive;
   this.id = id;
   this.label = label;
   this.submenu = submenu;
   this.type = type;
   this.visible = visible;
 }
 
 module.exports = MenuItem;
--- a/devtools/client/framework/menu.js
+++ b/devtools/client/framework/menu.js
@@ -128,16 +128,19 @@ Menu.prototype._createMenuItems = functi
       let menusep = doc.createElement("menuseparator");
       parent.appendChild(menusep);
     } else {
       let menuitem = doc.createElement("menuitem");
       menuitem.setAttribute("label", item.label);
       menuitem.addEventListener("command", () => {
         item.click();
       });
+      menuitem.addEventListener("DOMMenuItemActive", () => {
+        item.DOMMenuItemActive();
+      });
 
       if (item.type === "checkbox") {
         menuitem.setAttribute("type", "checkbox");
       }
       if (item.type === "radio") {
         menuitem.setAttribute("type", "radio");
       }
       if (item.disabled) {
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -2031,16 +2031,19 @@ Toolbox.prototype = {
 
       // Create menu item.
       menu.append(new MenuItem({
         label,
         type: "radio",
         checked,
         click: () => {
           this.onSelectFrame(frame.id);
+        },
+        DOMMenuItemActive: () => {
+          this.onHightlightFrame(frame.id);
         }
       }));
     });
 
     menu.once("open").then(() => {
       this.frameButton.isChecked = true;
     });
 
@@ -2070,16 +2073,22 @@ Toolbox.prototype = {
     let packet = {
       to: this._target.form.actor,
       type: "switchToFrame",
       windowId: frameId
     };
     this._target.client.request(packet);
   },
 
+  onHightlightFrame: async function (frameId) {
+    await this.initInspector();
+    let frameActor = await this.walker.getNodeActorFromWindowID(frameId);
+    this._highlighter.showBoxModel(frameActor);
+  },
+
   /**
    * A handler for 'frameUpdate' packets received from the backend.
    * Following properties might be set on the packet:
    *
    * destroyAll {Boolean}: All frames have been destroyed.
    * selected {Number}: A frame has been selected
    * frames {Array}: list of frames. Every frame can have:
    *                 id {Number}: frame ID
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -2611,16 +2611,33 @@ var WalkerActor = protocol.ActorClassWit
     // as document.documentElement (the <html> node)
     if (rawNode.defaultView && rawNode === rawNode.defaultView.document) {
       rawNode = rawNode.documentElement;
     }
 
     return this.attachElement(rawNode);
   },
 
+  getNodeActorFromWindowID: function (windowID) {
+    let win;
+
+    try {
+      win = Services.wm.getOuterWindowWithId(windowID);
+    } catch (e) {
+      // ignore
+    }
+
+    if (!win) {
+      return { error: "noWindow",
+               message: "The related docshell is destroyed or not found" };
+    }
+
+    return this.attachElement(win.frameElement);
+  },
+
   /**
    * Given a StyleSheetActor (identified by its ID), commonly used in the
    * style-editor, get its ownerNode and return the corresponding walker's
    * NodeActor.
    * Note that getNodeFromActor was added later and can now be used instead.
    */
   getStyleSheetOwnerNode: function (styleSheetActorID) {
     return this.getNodeFromActor(styleSheetActorID, ["ownerNode"]);
--- a/devtools/shared/fronts/inspector.js
+++ b/devtools/shared/fronts/inspector.js
@@ -626,16 +626,24 @@ const WalkerFront = FrontClassWithSpec(w
   getNodeActorFromObjectActor: custom(function (objectActorID) {
     return this._getNodeActorFromObjectActor(objectActorID).then(response => {
       return response ? response.node : null;
     });
   }, {
     impl: "_getNodeActorFromObjectActor"
   }),
 
+  getNodeActorFromWindowID: custom(function (windowID) {
+    return this._getNodeActorFromWindowID(windowID).then(response => {
+      return response ? response.node : null;
+    });
+  }, {
+    impl: "_getNodeActorFromWindowID"
+  }),
+
   getStyleSheetOwnerNode: custom(function (styleSheetActorID) {
     return this._getStyleSheetOwnerNode(styleSheetActorID).then(response => {
       return response ? response.node : null;
     });
   }, {
     impl: "_getStyleSheetOwnerNode"
   }),
 
--- a/devtools/shared/specs/inspector.js
+++ b/devtools/shared/specs/inspector.js
@@ -347,16 +347,24 @@ const walkerSpec = generateActorSpec({
     getNodeActorFromObjectActor: {
       request: {
         objectActorID: Arg(0, "string")
       },
       response: {
         nodeFront: RetVal("nullable:disconnectedNode")
       }
     },
+    getNodeActorFromWindowID: {
+      request: {
+        windowID: Arg(0, "string")
+      },
+      response: {
+        nodeFront: RetVal("nullable:disconnectedNode")
+      }
+    },
     getStyleSheetOwnerNode: {
       request: {
         styleSheetActorID: Arg(0, "string")
       },
       response: {
         ownerNode: RetVal("nullable:disconnectedNode")
       }
     },