--- a/devtools/server/actors/highlighters/flexbox.js
+++ b/devtools/server/actors/highlighters/flexbox.js
@@ -251,29 +251,67 @@ class FlexboxHighlighter extends AutoRef
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.
+ * flex container 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
- // to any alignment and size changes. This is blocked on Bug 1414920 that implements
- // a platform API to retrieve the flex container and flex item information.
+ if (!this.computedStyle) {
+ this.computedStyle = getComputedStyle(this.currentNode);
+ }
+
+ let oldFlexData = this.flexData;
+ this.flexData = this.currentNode.getAsFlexContainer();
+ let hasFlexDataChanged = compareFlexData(oldFlexData, this.flexData);
+
+ let oldAlignItems = this.alignItemsValue;
+ this.alignItemsValue = this.computedStyle.alignItems;
+ let newAlignItems = this.alignItemsValue;
+
+ let oldFlexBasis = this.flexBasis;
+ this.flexBasis = this.computedStyle.flexBasis;
+ let newFlexBasis = this.flexBasis;
- return hasMoved;
+ let oldFlexDirection = this.flexDirection;
+ this.flexDirection = this.computedStyle.flexDirection;
+ let newFlexDirection = this.flexDirection;
+
+ let oldFlexWrap = this.flexWrap;
+ this.flexWrap = this.computedStyle.flexWrap;
+ let newFlexWrap = this.flexWrap;
+
+ let oldJustifyContent = this.justifyContentValue;
+ this.justifyContentValue = this.computedStyle.justifyContent;
+ let newJustifyContent = this.justifyContentValue;
+
+ return hasMoved ||
+ hasFlexDataChanged ||
+ oldAlignItems !== newAlignItems ||
+ oldFlexBasis !== newFlexBasis ||
+ oldFlexDirection !== newFlexDirection ||
+ oldFlexWrap !== newFlexWrap ||
+ oldJustifyContent !== newJustifyContent;
}
_hide() {
+ this.alignItemsValue = null;
+ this.computedStyle = null;
+ this.flexBasis = null;
+ this.flexData = null;
+ this.flexDirection = null;
+ this.flexWrap = null;
+ this.justifyContentValue = null;
+
setIgnoreLayoutChanges(true);
this._hideFlexbox();
setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
}
_hideFlexbox() {
this.getElement("canvas").setAttribute("hidden", "true");
}
@@ -319,43 +357,41 @@ class FlexboxHighlighter extends AutoRef
this.clearCache();
if (isTopLevel) {
this.hide();
}
}
renderAlignItemLine() {
- if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+ if (!this.flexData || !this.currentQuads.content || !this.currentQuads.content[0]) {
return;
}
let { devicePixelRatio } = this.win;
let lineWidth = getDisplayPixelRatio(this.win);
let offset = (lineWidth / 2) % 1;
let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
this.ctx.save();
this.ctx.translate(offset - canvasX, offset - canvasY);
this.ctx.setLineDash(FLEXBOX_LINES_PROPERTIES.alignItems.lineDash);
this.ctx.lineWidth = lineWidth * 3;
this.ctx.strokeStyle = DEFAULT_COLOR;
let { bounds } = this.currentQuads.content[0];
- let flexLines = this.currentNode.getAsFlexContainer().getLines();
- let computedStyle = getComputedStyle(this.currentNode);
- let alignItemsType = computedStyle.getPropertyValue("align-items");
- let isColumn = computedStyle.getPropertyValue("flex-direction").startsWith("column");
+ let flexLines = this.flexData.getLines();
+ let isColumn = this.flexDirection.startsWith("column");
let options = { matrix: this.currentMatrix };
for (let flexLine of flexLines) {
let { crossStart, crossSize } = flexLine;
- switch (alignItemsType) {
+ switch (this.alignItemsValue) {
case "baseline":
case "first baseline":
let { firstBaselineOffset } = flexLine;
if (firstBaselineOffset < 0) {
break;
}
@@ -482,55 +518,53 @@ class FlexboxHighlighter extends AutoRef
this.ctx.stroke();
this.ctx.restore();
}
/**
* Renders the flex basis for a given flex item.
*/
renderFlexItemBasis(flexItem, left, top, right, bottom, boundsWidth) {
- let computedStyle = getComputedStyle(flexItem);
-
- if (!computedStyle) {
+ if (!this.computedStyle) {
return;
}
- let basis = computedStyle.getPropertyValue("flex-basis");
+ let basis = this.flexBasis;
if (basis.endsWith("px")) {
right = Math.round(left + parseFloat(basis));
} else if (basis.endsWith("%")) {
basis = parseFloat(basis) / 100 * boundsWidth;
right = Math.round(left + basis);
}
this.ctx.fillStyle = BASIS_FILL_COLOR;
drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
this.ctx.fill();
}
renderFlexItems() {
- if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+ if (!this.flexData || !this.currentQuads.content || !this.currentQuads.content[0]) {
return;
}
let { devicePixelRatio } = this.win;
let lineWidth = getDisplayPixelRatio(this.win);
let offset = (lineWidth / 2) % 1;
let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
this.ctx.save();
this.ctx.translate(offset - canvasX, offset - canvasY);
this.ctx.setLineDash(FLEXBOX_LINES_PROPERTIES.item.lineDash);
this.ctx.lineWidth = lineWidth;
this.ctx.strokeStyle = DEFAULT_COLOR;
let { bounds } = this.currentQuads.content[0];
- let flexLines = this.currentNode.getAsFlexContainer().getLines();
+ let flexLines = this.flexData.getLines();
for (let flexLine of flexLines) {
let flexItems = flexLine.getItems();
for (let flexItem of flexItems) {
let { node } = flexItem;
let quads = getAdjustedQuads(this.win, node, "border");
@@ -552,35 +586,34 @@ class FlexboxHighlighter extends AutoRef
this.renderFlexItemBasis(node, left, top, right, bottom, bounds.width);
}
}
this.ctx.restore();
}
renderFlexLines() {
- if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+ if (!this.flexData || !this.currentQuads.content || !this.currentQuads.content[0]) {
return;
}
let { devicePixelRatio } = this.win;
let lineWidth = getDisplayPixelRatio(this.win);
let offset = (lineWidth / 2) % 1;
let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
this.ctx.save();
this.ctx.translate(offset - canvasX, offset - canvasY);
this.ctx.lineWidth = lineWidth;
this.ctx.strokeStyle = DEFAULT_COLOR;
let { bounds } = this.currentQuads.content[0];
- let flexLines = this.currentNode.getAsFlexContainer().getLines();
- let computedStyle = getComputedStyle(this.currentNode);
- let isColumn = computedStyle.getPropertyValue("flex-direction").startsWith("column");
+ let flexLines = this.flexData.getLines();
+ let isColumn = this.flexDirection.startsWith("column");
let options = { matrix: this.currentMatrix };
for (let flexLine of flexLines) {
let { crossStart, crossSize } = flexLine;
if (isColumn) {
clearRect(this.ctx, crossStart, 0, crossStart + crossSize, bounds.height,
this.currentMatrix);
@@ -615,24 +648,23 @@ class FlexboxHighlighter extends AutoRef
}
}
}
this.ctx.restore();
}
renderJustifyContent() {
- if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+ if (!this.flexData || !this.currentQuads.content || !this.currentQuads.content[0]) {
return;
}
let { bounds } = this.currentQuads.content[0];
- let flexLines = this.currentNode.getAsFlexContainer().getLines();
- let computedStyle = getComputedStyle(this.currentNode);
- let isColumn = computedStyle.getPropertyValue("flex-direction").startsWith("column");
+ let flexLines = this.flexData.getLines();
+ let isColumn = this.flexDirection.startsWith("column");
for (let flexLine of flexLines) {
let { crossStart, crossSize } = flexLine;
let flexItems = flexLine.getItems();
let mainStart = 0;
for (let flexItem of flexItems) {
let { node } = flexItem;
@@ -703,9 +735,67 @@ class FlexboxHighlighter extends AutoRef
root.setAttribute("style",
`position: absolute; width: ${width}px; height: ${height}px; overflow: hidden`);
setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
return true;
}
}
+/**
+ * Returns whether or not the flex data has changed.
+ *
+ * @param {Flex} oldFlexData
+ * The old Flex data object.
+ * @param {Flex} newFlexData
+ * The new Flex data object.
+ * @return {Boolean} true if the flex data has changed and false otherwise.
+ */
+function compareFlexData(oldFlexData, newFlexData) {
+ if (!oldFlexData || !newFlexData) {
+ return true;
+ }
+
+ const oldLines = oldFlexData.getLines();
+ const newLines = newFlexData.getLines();
+
+ if (oldLines.length !== newLines.length) {
+ return true;
+ }
+
+ for (let i = 0; i < oldLines.length; i++) {
+ let oldLine = oldLines[i];
+ let newLine = newLines[i];
+
+ if (oldLine.crossSize !== newLine.crossSize ||
+ oldLine.crossStart !== newLine.crossStart ||
+ oldLine.firstBaselineOffset !== newLine.firstBaselineOffset ||
+ oldLine.growthState !== newLine.growthState ||
+ oldLine.lastBaselineOffset !== newLine.lastBaselineOffset) {
+ return true;
+ }
+
+ let oldItems = oldLine.getItems();
+ let newItems = newLine.getItems();
+
+ if (oldItems.length !== newItems.length) {
+ return true;
+ }
+
+ for (let j = 0; j < oldItems.length; j++) {
+ let oldItem = oldItems[j];
+ let newItem = newItems[j];
+
+ if (oldItem.crossMaxSize !== newItem.crossMaxSize ||
+ oldItem.crossMinSize !== newItem.crossMinSize ||
+ oldItem.mainBaseSize !== newItem.mainBaseSize ||
+ oldItem.mainDeltaSize !== newItem.mainDeltaSize ||
+ oldItem.mainMaxSize !== newItem.mainMaxSize ||
+ oldItem.mainMinSize !== newItem.mainMinSize) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
exports.FlexboxHighlighter = FlexboxHighlighter;