Bug 1409968 - Implement FlexboxActor for fetching all flexbox containers. r=pbro
MozReview-Commit-ID: FtDXlpBALhB
--- 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;