--- a/devtools/client/inspector/grids/components/GridOutline.js
+++ b/devtools/client/inspector/grids/components/GridOutline.js
@@ -1,32 +1,42 @@
/* 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 Services = require("Services");
+const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { PureComponent } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { getStr } = require("devtools/client/inspector/layout/utils/l10n");
+const {
+ getWritingModeMatrix,
+ getCSSMatrixTransform,
+} = require("devtools/shared/layout/dom-matrix-2d");
const Types = require("../types");
// The delay prior to executing the grid cell highlighting.
const GRID_HIGHLIGHTING_DEBOUNCE = 50;
// Prefs for the max number of rows/cols a grid container can have for
// the outline to display.
const GRID_OUTLINE_MAX_ROWS_PREF =
Services.prefs.getIntPref("devtools.gridinspector.gridOutlineMaxRows");
const GRID_OUTLINE_MAX_COLUMNS_PREF =
Services.prefs.getIntPref("devtools.gridinspector.gridOutlineMaxColumns");
+// Boolean pref to enable adjustment for writing mode and RTL content.
+DevToolsUtils.defineLazyGetter(this, "WRITING_MODE_ADJUST_ENABLED", () => {
+ return Services.prefs.getBoolPref("devtools.highlighter.writingModeAdjust");
+});
+
// Move SVG grid to the right 100 units, so that it is not flushed against the edge of
// layout border
const TRANSLATE_X = 0;
const TRANSLATE_Y = 0;
const GRID_CELL_SCALE_FACTOR = 50;
const VIEWPORT_MIN_HEIGHT = 100;
@@ -91,17 +101,17 @@ class GridOutline extends PureComponent
const {
grids,
onShowGridAreaHighlight,
onShowGridCellHighlight,
} = this.props;
const name = target.dataset.gridAreaName;
const id = target.dataset.gridId;
const fragmentIndex = target.dataset.gridFragmentIndex;
- const color = target.closest(".grid-cell-group").dataset.gridLineColor;
+ const color = target.closest(".grid-outline-group").dataset.gridLineColor;
const rowNumber = target.dataset.gridRow;
const columnNumber = target.dataset.gridColumn;
onShowGridAreaHighlight(grids[id].nodeFront, null, color);
onShowGridCellHighlight(grids[id].nodeFront, color);
if (hide) {
return;
@@ -177,16 +187,24 @@ class GridOutline extends PureComponent
height += GRID_CELL_SCALE_FACTOR * (rows.tracks[i].breadth / 100);
}
let width = 0;
for (let i = 0; i < cols.lines.length - 1; i++) {
width += GRID_CELL_SCALE_FACTOR * (cols.tracks[i].breadth / 100);
}
+ if (WRITING_MODE_ADJUST_ENABLED) {
+ // All writing modes other than horizontal-tb (the initial value) involve a 90 deg
+ // rotation, so swap width and height.
+ if (grid.writingMode != "horizontal-tb") {
+ [ width, height ] = [ height, width ];
+ }
+ }
+
return { width, height };
}
/**
* Displays a message text "Cannot show outline for this grid".
*/
renderCannotShowOutlineText() {
return dom.div(
@@ -239,22 +257,40 @@ class GridOutline extends PureComponent
rectangles.push(gridCell);
x += width;
}
x = 0;
y += height;
}
+ // Transform the cells as needed to match the grid container's writing mode.
+ let cellGroupStyle = {};
+
+ if (WRITING_MODE_ADJUST_ENABLED) {
+ let writingModeMatrix = getWritingModeMatrix(this.state, grid);
+ cellGroupStyle.transform = getCSSMatrixTransform(writingModeMatrix);
+ }
+
+ let cellGroup = dom.g(
+ {
+ id: "grid-cell-group",
+ style: cellGroupStyle,
+ },
+ rectangles
+ );
+
// Draw a rectangle that acts as the grid outline border.
const border = this.renderGridOutlineBorder(this.state.width, this.state.height,
color);
- rectangles.unshift(border);
- return rectangles;
+ return [
+ border,
+ cellGroup,
+ ];
}
/**
* Renders the grid cell of a grid fragment.
*
* @param {Number} id
* The grid id stored on the grid fragment
* @param {Number} gridFragmentIndex
@@ -273,18 +309,18 @@ class GridOutline extends PureComponent
* The width of grid cell.
* @param {Number} height
* The height of the grid cell.
*/
renderGridCell(id, gridFragmentIndex, x, y, rowNumber, columnNumber, color,
gridAreaName, width, height) {
return dom.rect(
{
- "key": `${id}-${rowNumber}-${columnNumber}`,
- "className": "grid-outline-cell",
+ key: `${id}-${rowNumber}-${columnNumber}`,
+ className: "grid-outline-cell",
"data-grid-area-name": gridAreaName,
"data-grid-fragment-index": gridFragmentIndex,
"data-grid-id": id,
"data-grid-row": rowNumber,
"data-grid-column": columnNumber,
x,
y,
width,
@@ -296,20 +332,20 @@ class GridOutline extends PureComponent
);
}
renderGridOutline(grid) {
let { color } = grid;
return dom.g(
{
- id: "grid-cell-group",
- "className": "grid-cell-group",
+ id: "grid-outline-group",
+ className: "grid-outline-group",
"data-grid-line-color": color,
- "style": { color }
+ style: { color }
},
this.renderGrid(grid)
);
}
renderGridOutlineBorder(borderWidth, borderHeight, color) {
return dom.rect(
{
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -327,19 +327,21 @@ class GridInspector {
let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
let color = this.getInitialGridColor(nodeFront, fallbackColor);
grids.push({
id: i,
actorID: grid.actorID,
color,
+ direction: grid.direction,
gridFragments: grid.gridFragments,
highlighted: nodeFront == this.highlighters.gridHighlighterShown,
nodeFront,
+ writingMode: grid.writingMode,
});
}
this.store.dispatch(updateGrids(grids));
}
/**
* Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
--- a/devtools/client/inspector/grids/test/browser.ini
+++ b/devtools/client/inspector/grids/test/browser.ini
@@ -26,11 +26,12 @@ support-files =
[browser_grids_grid-list-on-mutation-element-removed.js]
[browser_grids_grid-list-toggle-multiple-grids.js]
[browser_grids_grid-list-toggle-single-grid.js]
[browser_grids_grid-outline-cannot-show-outline.js]
[browser_grids_grid-outline-highlight-area.js]
[browser_grids_grid-outline-highlight-cell.js]
[browser_grids_grid-outline-selected-grid.js]
[browser_grids_grid-outline-updates-on-grid-change.js]
+[browser_grids_grid-outline-writing-mode.js]
[browser_grids_highlighter-setting-rules-grid-toggle.js]
[browser_grids_number-of-css-grids-telemetry.js]
[browser_grids_restored-after-reload.js]
--- a/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-area.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-area.js
@@ -37,35 +37,35 @@ add_task(function* () {
// Don't track reflows since this might cause intermittent failures.
inspector.reflowTracker.untrackReflows(gridInspector, gridInspector.onReflow);
let gridList = doc.getElementById("grid-list");
let checkbox = gridList.children[0].querySelector("input");
info("Toggling ON the CSS grid highlighter from the layout panel.");
let onHighlighterShown = highlighters.once("grid-highlighter-shown");
- let onGridOutlineRendered = waitForDOM(doc, "#grid-cell-group rect", 3);
+ let onGridOutlineRendered = waitForDOM(doc, "#grid-cell-group rect", 2);
let onCheckboxChange = waitUntilState(store, state =>
state.grids.length == 1 &&
state.grids[0].highlighted);
checkbox.click();
yield onCheckboxChange;
yield onHighlighterShown;
let elements = yield onGridOutlineRendered;
- let gridCellA = elements[1];
+ let gridCellA = elements[0];
info("Hovering over grid cell A in the grid outline.");
let onCellAHighlight = highlighters.once("grid-highlighter-shown",
(event, nodeFront, options) => {
info("Checking the grid highlighter options for the show grid area" +
"and cell parameters.");
const { showGridCell, showGridArea } = options;
const { gridFragmentIndex, rowNumber, columnNumber } = showGridCell;
is(gridFragmentIndex, 0, "Should be the first grid fragment index.");
is(rowNumber, 1, "Should be the first grid row.");
is(columnNumber, 1, "Should be the first grid column.");
is(showGridArea, "header", "Grid area name should be 'header'.");
});
- EventUtils.synthesizeMouse(gridCellA, 5, 5, {type: "mouseover"}, doc.defaultView);
+ EventUtils.synthesizeMouse(gridCellA, 1, 1, {type: "mouseover"}, doc.defaultView);
yield onCellAHighlight;
});
--- a/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-cell.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-cell.js
@@ -28,33 +28,33 @@ add_task(function* () {
// Don't track reflows since this might cause intermittent failures.
inspector.reflowTracker.untrackReflows(gridInspector, gridInspector.onReflow);
let gridList = doc.getElementById("grid-list");
let checkbox = gridList.children[0].querySelector("input");
info("Toggling ON the CSS grid highlighter from the layout panel.");
let onHighlighterShown = highlighters.once("grid-highlighter-shown");
- let onGridOutlineRendered = waitForDOM(doc, "#grid-cell-group rect", 3);
+ let onGridOutlineRendered = waitForDOM(doc, "#grid-cell-group rect", 2);
let onCheckboxChange = waitUntilState(store, state =>
state.grids.length == 1 &&
state.grids[0].highlighted);
checkbox.click();
yield onCheckboxChange;
yield onHighlighterShown;
let elements = yield onGridOutlineRendered;
- let gridCellA = elements[1];
+ let gridCellA = elements[0];
info("Hovering over grid cell A in the grid outline.");
let onCellAHighlight = highlighters.once("grid-highlighter-shown",
(event, nodeFront, options) => {
info("Checking show grid cell options are correct.");
const { showGridCell } = options;
const { gridFragmentIndex, rowNumber, columnNumber } = showGridCell;
is(gridFragmentIndex, 0, "Should be the first grid fragment index.");
is(rowNumber, 1, "Should be the first grid row.");
is(columnNumber, 1, "Should be the first grid column.");
});
- EventUtils.synthesizeMouse(gridCellA, 10, 5, {type: "mouseover"}, doc.defaultView);
+ EventUtils.synthesizeMouse(gridCellA, 1, 1, {type: "mouseover"}, doc.defaultView);
yield onCellAHighlight;
});
--- a/devtools/client/inspector/grids/test/browser_grids_grid-outline-selected-grid.js
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-selected-grid.js
@@ -32,17 +32,17 @@ add_task(function* () {
ok(!doc.getElementById("grid-outline-container"),
"There should be no grid outline shown.");
info("Toggling ON the CSS grid highlighter from the layout panel.");
let onHighlighterShown = highlighters.once("grid-highlighter-shown");
let onCheckboxChange = waitUntilState(store, state =>
state.grids.length == 1 &&
state.grids[0].highlighted);
- let onGridOutlineRendered = waitForDOM(doc, "#grid-cell-group rect", 4);
+ let onGridOutlineRendered = waitForDOM(doc, "#grid-cell-group rect", 3);
checkbox.click();
yield onHighlighterShown;
yield onCheckboxChange;
let elements = yield onGridOutlineRendered;
info("Checking the grid outline is shown.");
- is(elements.length, 4, "Grid outline is shown.");
+ is(elements.length, 3, "Grid outline is shown.");
});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-writing-mode.js
@@ -0,0 +1,139 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid outline adjusts to match the container's writing mode.
+
+const TEST_URI = `
+ <style type='text/css'>
+ .grid {
+ display: grid;
+ width: 400px;
+ height: 300px;
+ }
+ .rtl {
+ direction: rtl;
+ }
+ .v-rl {
+ writing-mode: vertical-rl;
+ }
+ .v-lr {
+ writing-mode: vertical-lr;
+ }
+ .s-rl {
+ writing-mode: sideways-rl;
+ }
+ .s-lr {
+ writing-mode: sideways-lr;
+ }
+ </style>
+ <div class="grid">
+ <div id="cella">Cell A</div>
+ <div id="cellb">Cell B</div>
+ <div id="cellc">Cell C</div>
+ </div>
+ <div class="grid rtl">
+ <div id="cella">Cell A</div>
+ <div id="cellb">Cell B</div>
+ <div id="cellc">Cell C</div>
+ </div>
+ <div class="grid v-rl">
+ <div id="cella">Cell A</div>
+ <div id="cellb">Cell B</div>
+ <div id="cellc">Cell C</div>
+ </div>
+ <div class="grid v-lr">
+ <div id="cella">Cell A</div>
+ <div id="cellb">Cell B</div>
+ <div id="cellc">Cell C</div>
+ </div>
+ <div class="grid s-rl">
+ <div id="cella">Cell A</div>
+ <div id="cellb">Cell B</div>
+ <div id="cellc">Cell C</div>
+ </div>
+ <div class="grid s-lr">
+ <div id="cella">Cell A</div>
+ <div id="cellb">Cell B</div>
+ <div id="cellc">Cell C</div>
+ </div>
+`;
+
+add_task(async function () {
+ await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+
+ let { inspector, gridInspector } = await openLayoutView();
+ let { document: doc } = gridInspector;
+ let { highlighters, store } = inspector;
+
+ info("Checking the initial state of the Grid Inspector.");
+ ok(!doc.getElementById("grid-outline-container"),
+ "There should be no grid outline shown.");
+
+ let elements;
+
+ elements = await enableGrid(doc, highlighters, store, 0);
+ is(elements[0].style.transform,
+ "matrix(1, 0, 0, 1, 0, 0)",
+ "Transform matches for horizontal-tb and ltr.");
+ await disableGrid(doc, highlighters, store, 0);
+
+ elements = await enableGrid(doc, highlighters, store, 1);
+ is(elements[0].style.transform,
+ "matrix(-1, 0, 0, 1, 200, 0)",
+ "Transform matches for horizontal-tb and rtl");
+ await disableGrid(doc, highlighters, store, 1);
+
+ elements = await enableGrid(doc, highlighters, store, 2);
+ is(elements[0].style.transform,
+ "matrix(6.12323e-17, 1, -1, 6.12323e-17, 200, 0)",
+ "Transform matches for vertical-rl and ltr");
+ await disableGrid(doc, highlighters, store, 2);
+
+ elements = await enableGrid(doc, highlighters, store, 3);
+ is(elements[0].style.transform,
+ "matrix(-6.12323e-17, 1, 1, 6.12323e-17, 0, 0)",
+ "Transform matches for vertical-lr and ltr");
+ await disableGrid(doc, highlighters, store, 3);
+
+ elements = await enableGrid(doc, highlighters, store, 4);
+ is(elements[0].style.transform,
+ "matrix(6.12323e-17, 1, -1, 6.12323e-17, 200, 0)",
+ "Transform matches for sideways-rl and ltr");
+ await disableGrid(doc, highlighters, store, 4);
+
+ elements = await enableGrid(doc, highlighters, store, 5);
+ is(elements[0].style.transform,
+ "matrix(6.12323e-17, -1, 1, 6.12323e-17, -9.18485e-15, 150)",
+ "Transform matches for sideways-lr and ltr");
+ await disableGrid(doc, highlighters, store, 5);
+});
+
+async function enableGrid(doc, highlighters, store, index) {
+ info(`Enabling the CSS grid highlighter for grid ${index}.`);
+ let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+ let onCheckboxChange = waitUntilState(store, state =>
+ state.grids.length == 6 &&
+ state.grids[index].highlighted);
+ let onGridOutlineRendered = waitForDOM(doc, "#grid-cell-group");
+ let gridList = doc.getElementById("grid-list");
+ gridList.children[index].querySelector("input").click();
+ await onHighlighterShown;
+ await onCheckboxChange;
+ return onGridOutlineRendered;
+}
+
+async function disableGrid(doc, highlighters, store, index) {
+ info(`Disabling the CSS grid highlighter for grid ${index}.`);
+ let onHighlighterShown = highlighters.once("grid-highlighter-hidden");
+ let onCheckboxChange = waitUntilState(store, state =>
+ state.grids.length == 6 &&
+ !state.grids[index].highlighted);
+ let onGridOutlineRemoved = waitForDOM(doc, "#grid-cell-group", 0);
+ let gridList = doc.getElementById("grid-list");
+ gridList.children[index].querySelector("input").click();
+ await onHighlighterShown;
+ await onCheckboxChange;
+ return onGridOutlineRemoved;
+}
--- a/devtools/client/inspector/grids/test/head.js
+++ b/devtools/client/inspector/grids/test/head.js
@@ -11,18 +11,20 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
this);
// Load the shared Redux helpers into this compartment.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
this);
+Services.prefs.setBoolPref("devtools.highlighter.writingModeAdjust", true);
Services.prefs.setIntPref("devtools.toolbox.footer.height", 350);
registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.highlighter.writingModeAdjust");
Services.prefs.clearUserPref("devtools.toolbox.footer.height");
});
const HIGHLIGHTER_TYPE = "CssGridHighlighter";
/**
* Simulate a color change in a given color picker tooltip.
*
--- a/devtools/client/inspector/grids/types.js
+++ b/devtools/client/inspector/grids/types.js
@@ -11,24 +11,30 @@ const PropTypes = require("devtools/clie
*/
exports.grid = {
// The id of the grid
id: PropTypes.number,
// The color for the grid overlay highlighter
color: PropTypes.string,
+ // The text direction of the grid container
+ direction: PropTypes.string,
+
// The grid fragment object of the grid container
gridFragments: PropTypes.array,
// Whether or not the grid highlighter is highlighting the grid
highlighted: PropTypes.bool,
// The node front of the grid container
nodeFront: PropTypes.object,
+
+ // The writing mode of the grid container
+ writingMode: PropTypes.string,
};
/**
* The grid highlighter settings on what to display in its grid overlay in the document.
*/
exports.highlighterSettings = {
// Whether or not the grid highlighter should show the grid line numbers
showGridLineNumbers: PropTypes.bool,
--- a/devtools/server/actors/highlighters/utils/canvas.js
+++ b/devtools/server/actors/highlighters/utils/canvas.js
@@ -5,21 +5,20 @@
"use strict";
const Services = require("Services");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const {
apply,
getNodeTransformationMatrix,
+ getWritingModeMatrix,
identity,
isIdentity,
multiply,
- reflectAboutY,
- rotate,
scale,
translate,
} = require("devtools/shared/layout/dom-matrix-2d");
const { getViewportDimensions } = require("devtools/shared/layout/utils");
const { getComputedStyle } = require("./markup");
// A set of utility functions for highlighters that render their content to a <canvas>
// element.
@@ -316,17 +315,21 @@ function getCurrentMatrix(element, windo
}
// Translate the origin based on the node's padding and border values.
currentMatrix = multiply(currentMatrix,
translate(paddingLeft + borderLeft, paddingTop + borderTop));
if (WRITING_MODE_ADJUST_ENABLED) {
// Adjust as needed to match the writing mode and text direction of the element.
- let writingModeMatrix = getWritingModeMatrix(element, computedStyle);
+ let size = {
+ width: element.offsetWidth,
+ height: element.offsetHeight,
+ };
+ let writingModeMatrix = getWritingModeMatrix(size, computedStyle);
if (!isIdentity(writingModeMatrix)) {
currentMatrix = multiply(currentMatrix, writingModeMatrix);
}
}
return { currentMatrix, hasNodeTransformations };
}
@@ -371,82 +374,16 @@ function getPointsFromDiagonal(x1, y1, x
].map(point => {
let transformedPoint = apply(matrix, point);
return { x: transformedPoint[0], y: transformedPoint[1] };
});
}
/**
- * Returns the matrix to rotate, translate, and reflect (if needed) from the element's
- * top-left origin into the actual writing mode and text direction applied to the element.
- *
- * @param {Element} element
- * The current element.
- * @param {CSSStyleDeclaration} computedStyle
- * The computed style for the element.
- * @return {Array}
- * The matrix with adjustments for writing mode and text direction, if any.
- */
-function getWritingModeMatrix(element, computedStyle) {
- let currentMatrix = identity();
- let { direction, writingMode } = computedStyle;
-
- switch (writingMode) {
- case "horizontal-tb":
- // This is the initial value. No further adjustment needed.
- break;
- case "vertical-rl":
- currentMatrix = multiply(
- translate(element.offsetWidth, 0),
- rotate(-Math.PI / 2)
- );
- break;
- case "vertical-lr":
- currentMatrix = multiply(
- reflectAboutY(),
- rotate(-Math.PI / 2)
- );
- break;
- case "sideways-rl":
- currentMatrix = multiply(
- translate(element.offsetWidth, 0),
- rotate(-Math.PI / 2)
- );
- break;
- case "sideways-lr":
- currentMatrix = multiply(
- rotate(Math.PI / 2),
- translate(-element.offsetHeight, 0)
- );
- break;
- default:
- console.error(`Unexpected writing-mode: ${writingMode}`);
- }
-
- switch (direction) {
- case "ltr":
- // This is the initial value. No further adjustment needed.
- break;
- case "rtl":
- let rowLength = element.offsetWidth;
- if (writingMode != "horizontal-tb") {
- rowLength = element.offsetHeight;
- }
- currentMatrix = multiply(currentMatrix, translate(rowLength, 0));
- currentMatrix = multiply(currentMatrix, reflectAboutY());
- break;
- default:
- console.error(`Unexpected direction: ${direction}`);
- }
-
- return currentMatrix;
-}
-
-/**
* Updates the <canvas> element's style in accordance with the current window's
* device pixel ratio, and the position calculated in `getCanvasPosition`. It also
* clears the drawing context. This is called on canvas update after a scroll event where
* `getCanvasPosition` updates the new canvasPosition.
*
* @param {Canvas} canvas
* The <canvas> element.
* @param {Object} canvasPosition
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -97,19 +97,24 @@ const GridActor = ActorClassWithSpec(gri
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);
+ // Record writing mode and text direction for use by the grid outline.
+ let { direction, writingMode } = CssLogic.getComputedStyle(this.containerEl);
+
let form = {
actor: this.actorID,
+ direction,
gridFragments: this.gridFragments,
+ writingMode,
};
// 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;
}
--- a/devtools/shared/fronts/layout.js
+++ b/devtools/shared/fronts/layout.js
@@ -46,20 +46,44 @@ const GridFront = FrontClassWithSpec(gri
if (!this._form.containerNodeActorID) {
return null;
}
return this.conn.getActor(this._form.containerNodeActorID);
},
/**
+ * Get the text direction of the grid container.
+ * Added in Firefox 60.
+ */
+ get direction() {
+ if (!this._form.direction) {
+ return "ltr";
+ }
+
+ return this._form.direction;
+ },
+
+ /**
* Getter for the grid fragments data.
*/
get gridFragments() {
return this._form.gridFragments;
},
+
+ /**
+ * Get the writing mode of the grid container.
+ * Added in Firefox 60.
+ */
+ get writingMode() {
+ if (!this._form.writingMode) {
+ return "horizontal-tb";
+ }
+
+ return this._form.writingMode;
+ },
});
const LayoutFront = FrontClassWithSpec(layoutSpec, {});
exports.FlexboxFront = FlexboxFront;
exports.GridFront = GridFront;
exports.LayoutFront = LayoutFront;
--- a/devtools/shared/layout/dom-matrix-2d.js
+++ b/devtools/shared/layout/dom-matrix-2d.js
@@ -207,22 +207,112 @@ exports.changeMatrixBase = changeMatrixB
* as second argument; considering the ancestor transformation too.
* If no ancestor is specified, it will returns the transformation matrix relative to the
* node's parent element.
*
* @param {DOMNode} node
* The node.
* @param {DOMNode} ancestor
* The ancestor of the node given.
- ** @return {Array}
+ * @return {Array}
* The transformation matrix.
*/
function getNodeTransformationMatrix(node, ancestor = node.parentElement) {
let { a, b, c, d, e, f } = ancestor.getTransformToParent()
.multiply(node.getTransformToAncestor(ancestor));
return [
a, c, e,
b, d, f,
0, 0, 1
];
}
exports.getNodeTransformationMatrix = getNodeTransformationMatrix;
+
+/**
+ * Returns the matrix to rotate, translate, and reflect (if needed) from the element's
+ * top-left origin into the actual writing mode and text direction applied to the element.
+ *
+ * @param {Object} size
+ * An element's untransformed `width` and `height`.
+ * @param {Object} style
+ * The computed `writingMode` and `direction` properties for the element.
+ * @return {Array}
+ * The matrix with adjustments for writing mode and text direction, if any.
+ */
+function getWritingModeMatrix(size, style) {
+ let currentMatrix = identity();
+ let { width, height } = size;
+ let { direction, writingMode } = style;
+
+ switch (writingMode) {
+ case "horizontal-tb":
+ // This is the initial value. No further adjustment needed.
+ break;
+ case "vertical-rl":
+ currentMatrix = multiply(
+ translate(width, 0),
+ rotate(-Math.PI / 2)
+ );
+ break;
+ case "vertical-lr":
+ currentMatrix = multiply(
+ reflectAboutY(),
+ rotate(-Math.PI / 2)
+ );
+ break;
+ case "sideways-rl":
+ currentMatrix = multiply(
+ translate(width, 0),
+ rotate(-Math.PI / 2)
+ );
+ break;
+ case "sideways-lr":
+ currentMatrix = multiply(
+ rotate(Math.PI / 2),
+ translate(-height, 0)
+ );
+ break;
+ default:
+ console.error(`Unexpected writing-mode: ${writingMode}`);
+ }
+
+ switch (direction) {
+ case "ltr":
+ // This is the initial value. No further adjustment needed.
+ break;
+ case "rtl":
+ let rowLength = width;
+ if (writingMode != "horizontal-tb") {
+ rowLength = height;
+ }
+ currentMatrix = multiply(currentMatrix, translate(rowLength, 0));
+ currentMatrix = multiply(currentMatrix, reflectAboutY());
+ break;
+ default:
+ console.error(`Unexpected direction: ${direction}`);
+ }
+
+ return currentMatrix;
+}
+exports.getWritingModeMatrix = getWritingModeMatrix;
+
+/**
+ * Convert from the matrix format used in this module:
+ * a, c, e,
+ * b, d, f,
+ * 0, 0, 1
+ * to the format used by the `matrix()` CSS transform function:
+ * a, b, c, d, e, f
+ *
+ * @param {Array} M
+ * The matrix in this module's 9 element format.
+ * @return {String}
+ * The matching 6 element CSS transform function.
+ */
+function getCSSMatrixTransform(M) {
+ let [
+ a, c, e,
+ b, d, f,
+ ] = M;
+ return `matrix(${a}, ${b}, ${c}, ${d}, ${e}, ${f})`;
+}
+exports.getCSSMatrixTransform = getCSSMatrixTransform;