Bug 1409968 - Implement FlexboxActor for fetching all flexbox containers. r=pbro draft
authorGabriel Luong <gabriel.luong@gmail.com>
Thu, 19 Oct 2017 01:43:36 -0400
changeset 683060 9ea0a34a4cb4afb981aa8b091d3e6614ee0d461d
parent 683052 64cecfd341a1ff9375316d3b3565124def78f257
child 736512 1ba4c7caa8f2a534dee465ae629fc68c4ec11e6e
push id85233
push userbmo:gl@mozilla.com
push dateThu, 19 Oct 2017 05:43:54 +0000
reviewerspbro
bugs1409968
milestone58.0a1
Bug 1409968 - Implement FlexboxActor for fetching all flexbox containers. r=pbro MozReview-Commit-ID: FtDXlpBALhB
devtools/server/actors/layout.js
devtools/shared/fronts/layout.js
devtools/shared/specs/layout.js
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -2,105 +2,211 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
 const { getStringifiableFragments } =
   require("devtools/server/actors/utils/css-grid-utils");
-const { gridSpec, layoutSpec } = require("devtools/shared/specs/layout");
+const { flexboxSpec, gridSpec, layoutSpec } = require("devtools/shared/specs/layout");
+
+loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
 
 /**
  * Set of actors the expose the CSS layout information to the devtools protocol clients.
  *
  * The |Layout| actor is the main entry point. It is used to get various CSS
  * layout-related information from the document.
  *
+ * The |FlexBox| actor provides the container node information to inspector the flexbox
+ * container.
+ *
  * The |Grid| actor provides the grid fragment information to inspect the grid container.
  */
 
+const FlexboxActor = ActorClassWithSpec(flexboxSpec, {
+  /**
+   * @param  {LayoutActor} layoutActor
+   *         The LayoutActor instance.
+   * @param  {DOMNode} containerEl
+   *         The flexbox container element.
+   */
+  initialize(layoutActor, containerEl) {
+    Actor.prototype.initialize.call(this, layoutActor.conn);
+
+    this.containerEl = containerEl;
+    this.walker = layoutActor.walker;
+  },
+
+  destroy() {
+    Actor.prototype.destroy.call(this);
+
+    this.containerEl = null;
+    this.walker = null;
+  },
+
+  form(detail) {
+    if (detail === "actorid") {
+      return this.actorID;
+    }
+
+    let form = {
+      actor: this.actorID,
+    };
+
+    // If the WalkerActor already knows the container element, then also return its
+    // ActorID so we avoid the client from doing another round trip to get it in many
+    // cases.
+    if (this.walker.hasNode(this.containerEl)) {
+      form.containerNodeActorID = this.walker.getNode(this.containerEl).actorID;
+    }
+
+    return form;
+  },
+});
+
 /**
  * The GridActor provides information about a given grid's fragment data.
  */
-var GridActor = ActorClassWithSpec(gridSpec, {
+const GridActor = ActorClassWithSpec(gridSpec, {
   /**
    * @param  {LayoutActor} layoutActor
    *         The LayoutActor instance.
    * @param  {DOMNode} containerEl
    *         The grid container element.
    */
-  initialize: function (layoutActor, containerEl) {
+  initialize(layoutActor, containerEl) {
     Actor.prototype.initialize.call(this, layoutActor.conn);
 
     this.containerEl = containerEl;
     this.walker = layoutActor.walker;
   },
 
-  destroy: function () {
+  destroy() {
     Actor.prototype.destroy.call(this);
 
     this.containerEl = null;
     this.gridFragments = null;
     this.walker = null;
   },
 
-  form: function (detail) {
+  form(detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
     // Seralize the grid fragment data into JSON so protocol.js knows how to write
     // and read the data.
     let gridFragments = this.containerEl.getGridFragments();
     this.gridFragments = getStringifiableFragments(gridFragments);
 
     let form = {
       actor: this.actorID,
-      gridFragments: this.gridFragments
+      gridFragments: this.gridFragments,
     };
 
     // If the WalkerActor already knows the container element, then also return its
     // ActorID so we avoid the client from doing another round trip to get it in many
     // cases.
     if (this.walker.hasNode(this.containerEl)) {
       form.containerNodeActorID = this.walker.getNode(this.containerEl).actorID;
     }
 
     return form;
   },
 });
 
 /**
  * The CSS layout actor provides layout information for the given document.
  */
-var LayoutActor = ActorClassWithSpec(layoutSpec, {
-  initialize: function (conn, tabActor, walker) {
+const LayoutActor = ActorClassWithSpec(layoutSpec, {
+  initialize(conn, tabActor, walker) {
     Actor.prototype.initialize.call(this, conn);
 
     this.tabActor = tabActor;
     this.walker = walker;
   },
 
-  destroy: function () {
+  destroy() {
     Actor.prototype.destroy.call(this);
 
     this.tabActor = null;
     this.walker = null;
   },
 
   /**
+   * Returns an array of FlexboxdActor objects for all the flexbox containers found by 
+   * iterating below the given rootNode.
+   *
+   * @param  {Node|NodeActor} rootNode
+   *         The root node to start iterating at.
+   * @return {Array} An array of FlexboxActor objects.
+   */
+  getFlexbox(rootNode) {
+    let flexboxes = [];
+
+    if (!rootNode) {
+      return flexboxes;
+    }
+
+    let treeWalker = this.walker.getDocumentWalker(rootNode);
+    while (treeWalker.nextNode()) {
+      let currentNode = treeWalker.currentNode;
+      let computedStyle = CssLogic.getComputedStyle(currentNode);
+
+      if (!computedStyle) {
+        continue;
+      }
+
+      if (computedStyle.display == "inline-flex" || computedStyle.display == "flex") {
+        let flexboxActor = new FlexboxActor(this, currentNode);
+        flexboxes.push(flexboxActor);
+      }
+    }
+
+    return flexboxes;
+  },
+
+  /**
+   * Returns an array of FlexboxActor objects for all existing flexbox containers found by
+   * iterating below the given rootNode and optionally including nested frames.
+   *
+   * @param  {NodeActor} rootNode
+   * @param  {Boolean} traverseFrames
+   *         Whether or not we should iterate through nested frames.
+   * @return {Array} An array of FlexboxActor objects.
+   */
+  getAllFlexbox(rootNode, traverseFrames) {
+    let flexboxes = []
+
+    if (!rootNode) {
+      return flexboxes;
+    }
+
+    if (!traverseFrames) {
+      return this.getFlexbox(rootNode.rawNode);
+    }
+
+    for (let {document} of this.tabActor.windows) {
+      flexboxes = [...flexboxes, ...this.getFlexbox(document.documentElement)];
+    }
+
+    return flexboxes;
+  },
+
+  /**
    * Returns an array of GridActor objects for all the grid containers found by iterating
    * below the given rootNode.
    *
    * @param  {Node|NodeActor} rootNode
    *         The root node to start iterating at.
    * @return {Array} An array of GridActor objects.
    */
-  getGrids: function (rootNode) {
+  getGrids(rootNode) {
     let grids = [];
 
     if (!rootNode) {
       return grids;
     }
 
     let treeWalker = this.walker.getDocumentWalker(rootNode);
     while (treeWalker.nextNode()) {
@@ -119,30 +225,30 @@ var LayoutActor = ActorClassWithSpec(lay
    * Returns an array of GridActor objects for all existing grid containers found by
    * iterating below the given rootNode and optionally including nested frames.
    *
    * @param  {NodeActor} rootNode
    * @param  {Boolean} traverseFrames
    *         Whether or not we should iterate through nested frames.
    * @return {Array} An array of GridActor objects.
    */
-  getAllGrids: function (rootNode, traverseFrames) {
+  getAllGrids(rootNode, traverseFrames) {
     let grids = [];
 
     if (!rootNode) {
       return grids;
     }
 
     if (!traverseFrames) {
       return this.getGrids(rootNode.rawNode);
     }
 
     for (let {document} of this.tabActor.windows) {
       grids = [...grids, ...this.getGrids(document.documentElement)];
     }
 
     return grids;
   },
-
 });
 
+exports.FlexboxActor = FlexboxActor;
 exports.GridActor = GridActor;
 exports.LayoutActor = LayoutActor;
--- a/devtools/shared/fronts/layout.js
+++ b/devtools/shared/fronts/layout.js
@@ -1,16 +1,38 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { FrontClassWithSpec } = require("devtools/shared/protocol");
-const { gridSpec, layoutSpec } = require("devtools/shared/specs/layout");
+const { flexboxSpec, gridSpec, layoutSpec } = require("devtools/shared/specs/layout");
+
+const FlexboxFront = FrontClassWithSpec(flexboxSpec, {
+  form: function (form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+    this._form = form;
+  },
+
+  /**
+   * In some cases, the FlexboxActor already knows the NodeActor ID of the node where the
+   * flexbox is located. In such cases, this getter returns the NodeFront for it.
+   */
+  get containerNodeFront() {
+    if (!this._form.containerNodeActorID) {
+      return null;
+    }
+
+    return this.conn.getActor(this._form.containerNodeActorID);
+  },
+});
 
 const GridFront = FrontClassWithSpec(gridSpec, {
   form: function (form, detail) {
     if (detail === "actorid") {
       this.actorID = form;
       return;
     }
     this._form = form;
@@ -28,15 +50,16 @@ const GridFront = FrontClassWithSpec(gri
     return this.conn.getActor(this._form.containerNodeActorID);
   },
 
   /**
    * Getter for the grid fragments data.
    */
   get gridFragments() {
     return this._form.gridFragments;
-  }
+  },
 });
 
 const LayoutFront = FrontClassWithSpec(layoutSpec, {});
 
+exports.FlexboxFront = FlexboxFront;
 exports.GridFront = GridFront;
 exports.LayoutFront = LayoutFront;
--- a/devtools/shared/specs/layout.js
+++ b/devtools/shared/specs/layout.js
@@ -1,32 +1,49 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Arg, generateActorSpec, RetVal } = require("devtools/shared/protocol");
 
+const flexboxSpec = generateActorSpec({
+  typeName: "flexbox",
+
+  methods: {},
+});
+
 const gridSpec = generateActorSpec({
   typeName: "grid",
 
   methods: {},
 });
 
 const layoutSpec = generateActorSpec({
   typeName: "layout",
 
   methods: {
+    getAllFlexbox: {
+      request: {
+        rootNode: Arg(0, "domnode"),
+        traverseFrames: Arg(1, "nullable:boolean")
+      },
+      response: {
+        flexboxes: RetVal("array:flexbox")
+      }
+    },
+
     getAllGrids: {
       request: {
         rootNode: Arg(0, "domnode"),
         traverseFrames: Arg(1, "nullable:boolean")
       },
       response: {
         grids: RetVal("array:grid")
       }
-    }
+    },
   },
 });
 
+exports.flexboxSpec = flexboxSpec;
 exports.gridSpec = gridSpec;
 exports.layoutSpec = layoutSpec;