new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_07.js
@@ -0,0 +1,117 @@
+/* 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";
+
+// Test that shapes are updated correctly for scaling on one axis in transform mode.
+
+const TEST_URL = URL_ROOT + "doc_inspector_highlighter_cssshapes.html";
+const HIGHLIGHTER_TYPE = "ShapesHighlighter";
+const SHAPE_IDS = ["#polygon-transform", "#ellipse"];
+
+add_task(function* () {
+ let inspector = yield openInspectorForURL(TEST_URL);
+ let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)(inspector);
+ let {testActor} = inspector;
+
+ yield testOneDimScale(testActor, helper);
+
+ helper.finalize();
+});
+
+function* testOneDimScale(testActor, helper) {
+ for (let shape of SHAPE_IDS) {
+ info(`Displaying ${shape}`);
+ yield helper.show(shape, {mode: "cssClipPath", transformMode: true});
+ let { mouse } = helper;
+
+ let { top, left, width, height } = yield getBoundingBoxInPx(testActor, helper, shape);
+
+ // if the top or left edges are not visible, move the shape so it is.
+ if (top < 0 || left < 0) {
+ let x = left + width / 2;
+ let y = top + height / 2;
+ let dx = Math.max(0, -left);
+ let dy = Math.max(0, -top);
+ yield mouse.down(x, y, shape);
+ yield mouse.move(x + dx, y + dy, shape);
+ yield mouse.up(x + dx, y + dy, shape);
+ yield testActor.reflow();
+ left += dx;
+ top += dy;
+ }
+ let dx = width / 10;
+ let dy = height / 10;
+
+ info("Scaling from w");
+ yield mouse.down(left, top + height / 2, shape);
+ yield mouse.move(left + dx, top + height / 2, shape);
+ yield mouse.up(left + dx, top + height / 2, shape);
+ yield testActor.reflow();
+
+ let wBB = yield getBoundingBoxInPx(testActor, helper, shape);
+ is(wBB.top, top, `${shape} top not moved down after w scale`);
+ isnot(wBB.left, left, `${shape} left moved right after w scale`);
+ isnot(wBB.width, width, `${shape} width reduced after w scale`);
+ is(wBB.height, height, `${shape} height not reduced after w scale`);
+
+ info("Scaling from e");
+ yield mouse.down(wBB.left + wBB.width, wBB.top + wBB.height / 2, shape);
+ yield mouse.move(wBB.left + wBB.width - dx, wBB.top + wBB.height / 2, shape);
+ yield mouse.up(wBB.left + wBB.width - dx, wBB.top + wBB.height / 2, shape);
+ yield testActor.reflow();
+
+ let eBB = yield getBoundingBoxInPx(testActor, helper, shape);
+ is(eBB.top, wBB.top, `${shape} top not moved down after e scale`);
+ is(eBB.left, wBB.left, `${shape} left not moved right after e scale`);
+ isnot(eBB.width, wBB.width, `${shape} width reduced after e scale`);
+ is(eBB.height, wBB.height, `${shape} height not reduced after e scale`);
+
+ info("Scaling from s");
+ yield mouse.down(eBB.left + eBB.width / 2, eBB.top + eBB.height, shape);
+ yield mouse.move(eBB.left + eBB.width / 2, eBB.top + eBB.height - dy, shape);
+ yield mouse.up(eBB.left + eBB.width / 2, eBB.top + eBB.height - dy, shape);
+ yield testActor.reflow();
+
+ let sBB = yield getBoundingBoxInPx(testActor, helper, shape);
+ is(sBB.top, eBB.top, `${shape} top not moved down after w scale`);
+ is(sBB.left, eBB.left, `${shape} left not moved right after w scale`);
+ is(sBB.width, eBB.width, `${shape} width not reduced after w scale`);
+ isnot(sBB.height, eBB.height, `${shape} height reduced after w scale`);
+
+ info("Scaling from n");
+ yield mouse.down(sBB.left + sBB.width / 2, sBB.top, shape);
+ yield mouse.move(sBB.left + sBB.width / 2, sBB.top + dy, shape);
+ yield mouse.up(sBB.left + sBB.width / 2, sBB.top + dy, shape);
+ yield testActor.reflow();
+
+ let nBB = yield getBoundingBoxInPx(testActor, helper, shape);
+ isnot(nBB.top, sBB.top, `${shape} top moved down after n scale`);
+ is(nBB.left, sBB.left, `${shape} left not moved right after n scale`);
+ is(nBB.width, sBB.width, `${shape} width reduced after n scale`);
+ isnot(nBB.height, sBB.height, `${shape} height not reduced after n scale`);
+ }
+}
+
+function* getBoundingBoxInPx(testActor, helper, shape = "#polygon") {
+ let bbTop = parseFloat(yield helper.getElementAttribute("shapes-bounding-box", "y"));
+ let bbLeft = parseFloat(yield helper.getElementAttribute("shapes-bounding-box", "x"));
+ let bbWidth = parseFloat(yield helper.getElementAttribute("shapes-bounding-box",
+ "width"));
+ let bbHeight = parseFloat(yield helper.getElementAttribute("shapes-bounding-box",
+ "height"));
+
+ let quads = yield testActor.getAllAdjustedQuads(shape);
+ let { width, height } = quads.content[0].bounds;
+ let computedStyle = yield helper.highlightedNode.getComputedStyle();
+ let paddingTop = parseFloat(computedStyle["padding-top"].value);
+ let paddingLeft = parseFloat(computedStyle["padding-left"].value);
+
+ return {
+ top: paddingTop + height * bbTop / 100,
+ left: paddingLeft + width * bbLeft / 100,
+ width: width * bbWidth / 100,
+ height: height * bbHeight / 100
+ };
+}
--- a/devtools/server/actors/highlighters/shapes.js
+++ b/devtools/server/actors/highlighters/shapes.js
@@ -521,63 +521,72 @@ class ShapesHighlighter extends AutoRefr
// 3) Scale each point by multiplying by the scaling proportion.
// 4) Translate the shape back such that the anchor is in its original position.
let { bb } = this[_dragging];
let { minX, minY, maxX, maxY } = bb;
let { width, height } = this.zoomAdjustedDimensions;
// How much points on each axis should be translated before scaling
- let transX = (type === "scale-se" || type === "scale-ne") ?
+ let transX = (type === "scale-se" || type === "scale-ne" || type === "scale-e") ?
minX / 100 * width : maxX / 100 * width;
- let transY = (type === "scale-se" || type === "scale-sw") ?
+ let transY = (type === "scale-se" || type === "scale-sw" || type === "scale-s") ?
minY / 100 * height : maxY / 100 * height;
let { percentX, percentY } = this.convertPageCoordsToPercent(x, y);
let { percentX: percentPageX,
percentY: percentPageY } = this.convertPageCoordsToPercent(pageX, pageY);
// distance from original click to current mouse position, in %
- let distanceX = (type === "scale-se" || type === "scale-ne") ?
+ let distanceX = (type === "scale-se" || type === "scale-ne" || type === "scale-e") ?
percentPageX - percentX : percentX - percentPageX;
- let distanceY = (type === "scale-se" || type === "scale-sw") ?
+ let distanceY = (type === "scale-se" || type === "scale-sw" || type === "scale-s") ?
percentPageY - percentY : percentY - percentPageY;
// scale = 1 + proportion of distance to bounding box width/height of shape
let scaleX = 1 + distanceX / (maxX - minX);
let scaleY = 1 + distanceY / (maxY - minY);
let scale = (scaleX + scaleY) / 2;
+ let axis = "xy";
+ if (type === "scale-e" || type === "scale-w") {
+ scale = scaleX;
+ axis = "x";
+ } else if (type === "scale-n" || type === "scale-s") {
+ scale = scaleY;
+ axis = "y";
+ }
if (this.shapeType === "polygon") {
- this._scalePolygon(pageX, pageY, transX, transY, scale);
+ this._scalePolygon(pageX, pageY, transX, transY, scale, axis);
} else if (this.shapeType === "circle") {
this._scaleCircle(pageX, pageY, transX, transY, scale);
} else if (this.shapeType === "ellipse") {
- this._scaleEllipse(pageX, pageY, transX, transY, scale);
+ this._scaleEllipse(pageX, pageY, transX, transY, scale, axis);
} else if (this.shapeType === "inset") {
this._scaleInset(pageX, pageY, transX, transY, scale);
}
}
}
/**
* Scale a polygon depending on mouse position after clicking on a corner handle.
* @param {Number} pageX the x coordinate of the mouse
* @param {Number} pageY the y coordinate of the mouse
* @param {Number} transX the number of pixels to translate on the x axis before scaling
* @param {Number} transY the number of pixels to translate on the y axis before scaling
* @param {Number} scale the proportion to scale by
+ * @param {String} axis the axis to scale on. "x", "y", or "xy" for both.
*/
- _scalePolygon(pageX, pageY, transX, transY, scale) {
+ _scalePolygon(pageX, pageY, transX, transY, scale, axis) {
let { pointsInfo } = this[_dragging];
let polygonDef = (this.fillRule) ? `${this.fillRule}, ` : "";
polygonDef += pointsInfo.map(point => {
let { unitX, unitY, valueX, valueY, ratioX, ratioY } = point;
let [newX, newY] = scalePoint(valueX, valueY, transX * ratioX,
- transY * ratioY, scale);
+ transY * ratioY, scale, axis);
return `${newX}${unitX} ${newY}${unitY}`;
}).join(", ");
polygonDef = (this.geometryBox) ? `polygon(${polygonDef}) ${this.geometryBox}` :
`polygon(${polygonDef})`;
this.currentNode.style.setProperty(this.property, polygonDef, "important");
}
@@ -608,23 +617,24 @@ class ShapesHighlighter extends AutoRefr
/**
* Scale an ellipse depending on mouse position after clicking on a corner handle.
* @param {Number} pageX the x coordinate of the mouse
* @param {Number} pageY the y coordinate of the mouse
* @param {Number} transX the number of pixels to translate on the x axis before scaling
* @param {Number} transY the number of pixels to translate on the y axis before scaling
* @param {Number} scale the proportion to scale by
+ * @param {String} axis the axis to scale on. "x", "y", or "xy" for both.
*/
- _scaleEllipse(pageX, pageY, transX, transY, scale) {
+ _scaleEllipse(pageX, pageY, transX, transY, scale, axis) {
let { unitX, unitY, unitRX, unitRY, valueX, valueY,
ratioX, ratioY, ratioRX, ratioRY } = this[_dragging];
let [newCx, newCy] = scalePoint(valueX, valueY, transX * ratioX,
- transY * ratioY, scale);
+ transY * ratioY, scale, axis);
// As part of scaling, the center is translated to be tangent to the lines y=0 & x=0.
// To get the new radii, we scale the new center back to that point and get the
// distances to the line x=0 and y=0.
let newRx = `${Math.abs((newCx / ratioX - transX) * ratioRX)}${unitRX}`;
let newRy = `${Math.abs((newCy / ratioY - transY) * ratioRY)}${unitRY}`;
newCx = `${newCx}${unitX}`;
newCy = `${newCy}${unitY}`;
@@ -1077,16 +1087,20 @@ class ShapesHighlighter extends AutoRefr
let centerY = (minY + maxY) / 2;
const points = [
{ pointName: "translate", x: centerX, y: centerY, cursor: "move" },
{ pointName: "scale-se", x: maxX, y: maxY, cursor: "nwse-resize" },
{ pointName: "scale-ne", x: maxX, y: minY, cursor: "nesw-resize" },
{ pointName: "scale-sw", x: minX, y: maxY, cursor: "nesw-resize" },
{ pointName: "scale-nw", x: minX, y: minY, cursor: "nwse-resize" },
+ { pointName: "scale-n", x: centerX, y: minY, cursor: "ns-resize" },
+ { pointName: "scale-s", x: centerX, y: maxY, cursor: "ns-resize" },
+ { pointName: "scale-e", x: maxX, y: centerY, cursor: "ew-resize" },
+ { pointName: "scale-w", x: minX, y: centerY, cursor: "ew-resize" }
];
for (let { pointName, x, y, cursor } of points) {
if (point === pointName) {
this._drawHoverMarker([[x, y]]);
this.setCursor(cursor);
}
}
@@ -1221,24 +1235,31 @@ class ShapesHighlighter extends AutoRefr
let { width, height } = this.zoomAdjustedDimensions;
let zoom = getCurrentZoom(this.win);
let clickRadiusX = BASE_MARKER_SIZE / zoom * 100 / width;
let clickRadiusY = BASE_MARKER_SIZE / zoom * 100 / height;
let centerX = (minX + maxX) / 2;
let centerY = (minY + maxY) / 2;
- const points = [
+ let points = [
{ point: "translate", x: centerX, y: centerY },
{ point: "scale-se", x: maxX, y: maxY },
{ point: "scale-ne", x: maxX, y: minY },
{ point: "scale-sw", x: minX, y: maxY },
{ point: "scale-nw", x: minX, y: minY },
];
+ if (this.shapeType === "polygon" || this.shapeType === "ellipse") {
+ points.push({ point: "scale-n", x: centerX, y: minY },
+ { point: "scale-s", x: centerX, y: maxY },
+ { point: "scale-e", x: maxX, y: centerY },
+ { point: "scale-w", x: minX, y: centerY });
+ }
+
for (let { point, x, y } of points) {
if (pageX >= x - clickRadiusX && pageX <= x + clickRadiusX &&
pageY >= y - clickRadiusY && pageY <= y + clickRadiusY) {
return point;
}
}
return "";
@@ -1874,16 +1895,20 @@ class ShapesHighlighter extends AutoRefr
boundingBox.setAttribute("width", maxX - minX);
boundingBox.setAttribute("height", maxY - minY);
boundingBox.removeAttribute("hidden");
let centerX = (minX + maxX) / 2;
let centerY = (minY + maxY) / 2;
let markerPoints = [[centerX, centerY], [minX, minY],
[maxX, minY], [minX, maxY], [maxX, maxY]];
+ if (this.shapeType === "polygon" || this.shapeType === "ellipse") {
+ markerPoints.push([minX, centerY], [maxX, centerY],
+ [centerX, minY], [centerX, maxY]);
+ }
this._drawMarkers(markerPoints, width, height, zoom);
if (this.shapeType === "polygon") {
let points = this.coordinates.map(point => point.join(",")).join(" ");
let polygonEl = this.getElement("polygon");
polygonEl.setAttribute("points", points);
polygonEl.removeAttribute("hidden");