--- a/devtools/server/actors/highlighters/flexbox.js
+++ b/devtools/server/actors/highlighters/flexbox.js
@@ -31,25 +31,28 @@ const FLEXBOX_LINES_PROPERTIES = {
},
"item": {
lineDash: [0, 0]
}
};
const FLEXBOX_CONTAINER_PATTERN_WIDTH = 14; // px
const FLEXBOX_CONTAINER_PATTERN_HEIGHT = 14; // px
+const FLEXBOX_JUSTIFYCONTENT_PATTERN_WIDTH = 7; // px
+const FLEXBOX_JUSTIFYCONTENT_PATTERN_HEIGHT = 7; // px
const FLEXBOX_CONTAINER_PATTERN_LINE_DISH = [5, 3]; // px
const BASIS_FILL_COLOR = "rgb(109, 184, 255, 0.4)";
/**
* Cached used by `FlexboxHighlighter.getFlexContainerPattern`.
*/
const gCachedFlexboxPattern = new Map();
const FLEXBOX = "flexbox";
+const JUSTIFY_CONTENT = "justify-content";
class FlexboxHighlighter extends AutoRefreshHighlighter {
constructor(highlighterEnv) {
super(highlighterEnv);
this.ID_CLASS_PREFIX = "flexbox-";
this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
@@ -125,16 +128,26 @@ class FlexboxHighlighter extends AutoRef
this.markup.destroy();
// Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
this.clearCache();
AutoRefreshHighlighter.prototype.destroy.call(this);
}
+ /**
+ * Draw the justify content for a given flex item (left, top, right, bottom) position.
+ */
+ drawJustifyContent(left, top, right, bottom) {
+ let { devicePixelRatio } = this.win;
+ this.ctx.fillStyle = this.getJustifyContentPattern(devicePixelRatio);
+ drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
+ this.ctx.fill();
+ }
+
get canvas() {
return this.getElement("canvas");
}
get ctx() {
return this.canvas.getCanvasContext("2d");
}
@@ -183,16 +196,62 @@ class FlexboxHighlighter extends AutoRef
let pattern = ctx.createPattern(canvas, "repeat");
flexboxPatternMap.set(FLEXBOX, pattern);
gCachedFlexboxPattern.set(devicePixelRatio, flexboxPatternMap);
return pattern;
}
/**
+ * Gets the flexbox justify content pattern used to render the justify content regions.
+ *
+ * @param {Number} devicePixelRatio
+ * The device pixel ratio we want the pattern for.
+ * @return {CanvasPattern} flex justify content pattern.
+ */
+ getJustifyContentPattern(devicePixelRatio) {
+ let flexboxPatternMap = null;
+
+ if (gCachedFlexboxPattern.has(devicePixelRatio)) {
+ flexboxPatternMap = gCachedFlexboxPattern.get(devicePixelRatio);
+ } else {
+ flexboxPatternMap = new Map();
+ }
+
+ if (gCachedFlexboxPattern.has(JUSTIFY_CONTENT)) {
+ return gCachedFlexboxPattern.get(JUSTIFY_CONTENT);
+ }
+
+ // Create the inversed diagonal lines pattern
+ // for the rendering the justify content gaps.
+ let canvas = createNode(this.win, { nodeType: "canvas" });
+ let width = canvas.width = FLEXBOX_JUSTIFYCONTENT_PATTERN_WIDTH * devicePixelRatio;
+ let height = canvas.height = FLEXBOX_JUSTIFYCONTENT_PATTERN_HEIGHT * devicePixelRatio;
+
+ let ctx = canvas.getContext("2d");
+ ctx.save();
+ ctx.setLineDash(FLEXBOX_CONTAINER_PATTERN_LINE_DISH);
+ ctx.beginPath();
+ ctx.translate(.5, .5);
+
+ ctx.moveTo(0, height);
+ ctx.lineTo(width, 0);
+
+ ctx.strokeStyle = DEFAULT_COLOR;
+ ctx.stroke();
+ ctx.restore();
+
+ let pattern = ctx.createPattern(canvas, "repeat");
+ flexboxPatternMap.set(JUSTIFY_CONTENT, pattern);
+ gCachedFlexboxPattern.set(devicePixelRatio, flexboxPatternMap);
+
+ return pattern;
+ }
+
+ /**
* The AutoRefreshHighlighter's _hasMoved method returns true only if the
* element's quads have changed. Override it so it also returns true if the
* element and its flex items have changed.
*/
_hasMoved() {
let hasMoved = AutoRefreshHighlighter.prototype._hasMoved.call(this);
// TODO: Implement a check of old and new flex container and flex items to react
@@ -310,20 +369,20 @@ class FlexboxHighlighter extends AutoRef
/**
* Renders the flex basis for a given flex item.
*/
renderFlexItemBasis(flexItem, left, top, right, bottom, boundsWidth) {
let computedStyle = getComputedStyle(flexItem);
let basis = computedStyle.getPropertyValue("flex-basis");
if (basis.endsWith("px")) {
- right = left + parseFloat(basis);
+ right = Math.round(left + parseFloat(basis));
} else if (basis.endsWith("%")) {
basis = parseFloat(basis) / 100 * boundsWidth;
- right = left + basis;
+ right = Math.round(left + basis);
}
this.ctx.fillStyle = BASIS_FILL_COLOR;
drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
this.ctx.fill();
}
renderFlexItems() {
@@ -351,28 +410,27 @@ class FlexboxHighlighter extends AutoRef
for (let flexItem of flexItems) {
let quads = getAdjustedQuads(this.win, flexItem, "border");
if (!quads.length) {
continue;
}
// Adjust the flex item bounds relative to the current quads.
let { bounds: flexItemBounds } = quads[0];
- let left = flexItemBounds.left - bounds.left;
- let top = flexItemBounds.top - bounds.top;
- let right = flexItemBounds.right - bounds.left;
- let bottom = flexItemBounds.bottom - bounds.top;
+ let left = Math.round(flexItemBounds.left - bounds.left);
+ let top = Math.round(flexItemBounds.top - bounds.top);
+ let right = Math.round(flexItemBounds.right - bounds.left);
+ let bottom = Math.round(flexItemBounds.bottom - bounds.top);
clearRect(this.ctx, left, top, right, bottom, this.currentMatrix);
drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
this.ctx.stroke();
this.renderFlexItemBasis(flexItem, left, top, right, bottom, bounds.width);
}
-
this.ctx.restore();
}
renderFlexLines() {
if (!this.currentQuads.content || !this.currentQuads.content[0]) {
return;
}
@@ -412,16 +470,86 @@ class FlexboxHighlighter extends AutoRef
crossStart + crossSize, this.currentMatrix);
this.ctx.stroke();
}
}
this.ctx.restore();
}
+ renderJudtifyContent() {
+ if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+ return;
+ }
+
+ let { bounds } = this.currentQuads.content[0];
+ let flexItems = this.currentNode.children;
+ let flexLines = this.currentNode.getAsFlexContainer().getLines();
+ let computedStyle = getComputedStyle(this.currentNode);
+ let direction = computedStyle.getPropertyValue("flex-direction");
+
+ // The way to render justify-content is that highlighting all the contents first, and
+ // clearing the ocupied and margin areas of flexItem.
+ // highlight all the contents first:
+ for (let flexLine of flexLines) {
+ let { crossStart, crossSize } = flexLine;
+
+ if (direction === "column" || direction === "column-reverse") {
+ this.drawJustifyContent(crossStart, 0, crossStart + crossSize, bounds.height);
+ } else {
+ this.drawJustifyContent(0, crossStart, bounds.width, crossStart + crossSize);
+ }
+ }
+
+ for (let flexItem of flexItems) {
+ let quads = getAdjustedQuads(this.win, flexItem, "border");
+ if (!quads.length) {
+ continue;
+ }
+
+ // Adjust the flex item bounds relative to the current quads.
+ let { bounds: flexItemBounds } = quads[0];
+ let left = Math.round(flexItemBounds.left - bounds.left);
+ let top = Math.round(flexItemBounds.top - bounds.top);
+ let right = Math.round(flexItemBounds.right - bounds.left);
+ let bottom = Math.round(flexItemBounds.bottom - bounds.top);
+ let flexItemComputedStyle = getComputedStyle(flexItem);
+
+ // clearing the ocupied and margin areas of flexItem:
+ for (let flexLine of flexLines) {
+ let { crossStart, crossSize } = flexLine;
+ crossSize = Math.round(crossSize);
+ crossStart = Math.round(crossStart);
+
+ if ((direction === "column" || direction === "column-reverse") &&
+ (crossStart <= left) && (left <= right) &&
+ (right <= crossSize + crossStart)) {
+ // Remove the margin area for justify-content
+ let marginTop = Math.round(parseFloat(
+ flexItemComputedStyle.getPropertyValue("margin-top")));
+ let marginBottom = Math.round(parseFloat(
+ flexItemComputedStyle.getPropertyValue("margin-bottom")));
+ clearRect(this.ctx, crossStart, top - marginTop, crossSize + crossStart,
+ bottom + marginBottom, this.currentMatrix);
+ break;
+ } else if ((crossStart <= top) && (top <= bottom) &&
+ (bottom <= crossSize + crossStart)) {
+ let marginLeft = Math.round(parseFloat(
+ flexItemComputedStyle.getPropertyValue("margin-left")));
+ let marginRight = Math.round(parseFloat(
+ flexItemComputedStyle.getPropertyValue("margin-right")));
+ clearRect(this.ctx, left - marginLeft, crossStart, right + marginRight,
+ crossSize + crossStart, this.currentMatrix);
+ break;
+ }
+ }
+ }
+ this.ctx.restore();
+ }
+
_update() {
setIgnoreLayoutChanges(true);
let root = this.getElement("root");
// Hide the root element and force the reflow in order to get the proper window's
// dimensions without increasing them.
root.setAttribute("style", "display: none");
@@ -436,16 +564,17 @@ class FlexboxHighlighter extends AutoRef
// Update the current matrix used in our canvas' rendering
let { currentMatrix, hasNodeTransformations } = getCurrentMatrix(this.currentNode,
this.win);
this.currentMatrix = currentMatrix;
this.hasNodeTransformations = hasNodeTransformations;
this.renderFlexContainerFill();
this.renderFlexLines();
+ this.renderJudtifyContent();
this.renderFlexItems();
this.renderFlexContainerBorder();
this._showFlexbox();
root.setAttribute("style",
`position: absolute; width: ${width}px; height: ${height}px; overflow: hidden`);