1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef WritingModes_h_
8 #define WritingModes_h_
9
10 #include <ostream>
11
12 #include "mozilla/ComputedStyle.h"
13 #include "mozilla/ComputedStyleInlines.h"
14 #include "mozilla/EnumeratedRange.h"
15
16 #include "nsRect.h"
17 #include "nsBidiUtils.h"
18
19 // It is the caller's responsibility to operate on logical-coordinate objects
20 // with matched writing modes. Failure to do so will be a runtime bug; the
21 // compiler can't catch it, but in debug mode, we'll throw an assertion.
22 // NOTE that in non-debug builds, a writing mode mismatch error will NOT be
23 // detected, yet the results will be nonsense (and may lead to further layout
24 // failures). Therefore, it is important to test (and fuzz-test) writing-mode
25 // support using debug builds.
26
27 // Methods in logical-coordinate classes that take another logical-coordinate
28 // object as a parameter should call CHECK_WRITING_MODE on it to verify that
29 // the writing modes match.
30 // (In some cases, there are internal (private) methods that don't do this;
31 // such methods should only be used by other methods that have already checked
32 // the writing modes.)
33 // The check ignores the StyleWritingMode::VERTICAL_SIDEWAYS and
34 // StyleWritingMode::TEXT_SIDEWAYS bit of writing mode, because
35 // this does not affect the interpretation of logical coordinates.
36
37 #define CHECK_WRITING_MODE(param) \
38 NS_ASSERTION(param.IgnoreSideways() == GetWritingMode().IgnoreSideways(), \
39 "writing-mode mismatch")
40
41 namespace mozilla {
42
43 namespace widget {
44 struct IMENotification;
45 } // namespace widget
46
47 // Logical axis, edge, side and corner constants for use in various places.
48 enum LogicalAxis : uint8_t {
49 eLogicalAxisBlock = 0x0,
50 eLogicalAxisInline = 0x1
51 };
52 enum LogicalEdge { eLogicalEdgeStart = 0x0, eLogicalEdgeEnd = 0x1 };
53 enum LogicalSide : uint8_t {
54 eLogicalSideBStart = (eLogicalAxisBlock << 1) | eLogicalEdgeStart, // 0x0
55 eLogicalSideBEnd = (eLogicalAxisBlock << 1) | eLogicalEdgeEnd, // 0x1
56 eLogicalSideIStart = (eLogicalAxisInline << 1) | eLogicalEdgeStart, // 0x2
57 eLogicalSideIEnd = (eLogicalAxisInline << 1) | eLogicalEdgeEnd // 0x3
58 };
AllLogicalSides()59 constexpr auto AllLogicalSides() {
60 return mozilla::MakeInclusiveEnumeratedRange(eLogicalSideBStart,
61 eLogicalSideIEnd);
62 }
63
64 enum LogicalCorner {
65 eLogicalCornerBStartIStart = 0,
66 eLogicalCornerBStartIEnd = 1,
67 eLogicalCornerBEndIEnd = 2,
68 eLogicalCornerBEndIStart = 3
69 };
70
71 // Physical axis constants.
72 enum PhysicalAxis { eAxisVertical = 0x0, eAxisHorizontal = 0x1 };
73
GetOrthogonalAxis(LogicalAxis aAxis)74 inline LogicalAxis GetOrthogonalAxis(LogicalAxis aAxis) {
75 return aAxis == eLogicalAxisBlock ? eLogicalAxisInline : eLogicalAxisBlock;
76 }
77
IsInline(LogicalSide aSide)78 inline bool IsInline(LogicalSide aSide) { return aSide & 0x2; }
IsBlock(LogicalSide aSide)79 inline bool IsBlock(LogicalSide aSide) { return !IsInline(aSide); }
IsEnd(LogicalSide aSide)80 inline bool IsEnd(LogicalSide aSide) { return aSide & 0x1; }
IsStart(LogicalSide aSide)81 inline bool IsStart(LogicalSide aSide) { return !IsEnd(aSide); }
82
GetAxis(LogicalSide aSide)83 inline LogicalAxis GetAxis(LogicalSide aSide) {
84 return IsInline(aSide) ? eLogicalAxisInline : eLogicalAxisBlock;
85 }
86
GetEdge(LogicalSide aSide)87 inline LogicalEdge GetEdge(LogicalSide aSide) {
88 return IsEnd(aSide) ? eLogicalEdgeEnd : eLogicalEdgeStart;
89 }
90
GetOppositeEdge(LogicalEdge aEdge)91 inline LogicalEdge GetOppositeEdge(LogicalEdge aEdge) {
92 // This relies on the only two LogicalEdge enum values being 0 and 1.
93 return LogicalEdge(1 - aEdge);
94 }
95
MakeLogicalSide(LogicalAxis aAxis,LogicalEdge aEdge)96 inline LogicalSide MakeLogicalSide(LogicalAxis aAxis, LogicalEdge aEdge) {
97 return LogicalSide((aAxis << 1) | aEdge);
98 }
99
GetOppositeSide(LogicalSide aSide)100 inline LogicalSide GetOppositeSide(LogicalSide aSide) {
101 return MakeLogicalSide(GetAxis(aSide), GetOppositeEdge(GetEdge(aSide)));
102 }
103
104 enum LogicalSideBits {
105 eLogicalSideBitsNone = 0,
106 eLogicalSideBitsBStart = 1 << eLogicalSideBStart,
107 eLogicalSideBitsBEnd = 1 << eLogicalSideBEnd,
108 eLogicalSideBitsIEnd = 1 << eLogicalSideIEnd,
109 eLogicalSideBitsIStart = 1 << eLogicalSideIStart,
110 eLogicalSideBitsBBoth = eLogicalSideBitsBStart | eLogicalSideBitsBEnd,
111 eLogicalSideBitsIBoth = eLogicalSideBitsIStart | eLogicalSideBitsIEnd,
112 eLogicalSideBitsAll = eLogicalSideBitsBBoth | eLogicalSideBitsIBoth
113 };
114
115 enum LineRelativeDir {
116 eLineRelativeDirOver = eLogicalSideBStart,
117 eLineRelativeDirUnder = eLogicalSideBEnd,
118 eLineRelativeDirLeft = eLogicalSideIStart,
119 eLineRelativeDirRight = eLogicalSideIEnd
120 };
121
122 /**
123 * mozilla::WritingMode is an immutable class representing a
124 * writing mode.
125 *
126 * It efficiently stores the writing mode and can rapidly compute
127 * interesting things about it for use in layout.
128 *
129 * Writing modes are computed from the CSS 'direction',
130 * 'writing-mode', and 'text-orientation' properties.
131 * See CSS3 Writing Modes for more information
132 * http://www.w3.org/TR/css3-writing-modes/
133 */
134 class WritingMode {
135 public:
136 /**
137 * Absolute inline flow direction
138 */
139 enum InlineDir {
140 eInlineLTR = 0x00, // text flows horizontally left to right
141 eInlineRTL = 0x02, // text flows horizontally right to left
142 eInlineTTB = 0x01, // text flows vertically top to bottom
143 eInlineBTT = 0x03, // text flows vertically bottom to top
144 };
145
146 /**
147 * Absolute block flow direction
148 */
149 enum BlockDir {
150 eBlockTB = 0x00, // horizontal lines stack top to bottom
151 eBlockRL = 0x01, // vertical lines stack right to left
152 eBlockLR = 0x05, // vertical lines stack left to right
153 };
154
155 /**
156 * Line-relative (bidi-relative) inline flow direction
157 */
158 enum BidiDir {
159 eBidiLTR = 0x00, // inline flow matches bidi LTR text
160 eBidiRTL = 0x10, // inline flow matches bidi RTL text
161 };
162
163 /**
164 * Unknown writing mode (should never actually be stored or used anywhere).
165 */
166 enum { eUnknownWritingMode = 0xff };
167
168 /**
169 * Return the absolute inline flow direction as an InlineDir
170 */
GetInlineDir()171 InlineDir GetInlineDir() const {
172 return InlineDir(mWritingMode.bits & eInlineMask);
173 }
174
175 /**
176 * Return the absolute block flow direction as a BlockDir
177 */
GetBlockDir()178 BlockDir GetBlockDir() const {
179 return BlockDir(mWritingMode.bits & eBlockMask);
180 }
181
182 /**
183 * Return the line-relative inline flow direction as a BidiDir
184 */
GetBidiDir()185 BidiDir GetBidiDir() const {
186 return BidiDir((mWritingMode & StyleWritingMode::RTL).bits);
187 }
188
189 /**
190 * Return true if the inline flow direction is against physical direction
191 * (i.e. right-to-left or bottom-to-top).
192 * This occurs when writing-mode is sideways-lr OR direction is rtl (but not
193 * if both of those are true).
194 */
IsInlineReversed()195 bool IsInlineReversed() const {
196 return !!(mWritingMode & StyleWritingMode::INLINE_REVERSED);
197 }
198
199 /**
200 * Return true if bidi direction is LTR. (Convenience method)
201 */
IsBidiLTR()202 bool IsBidiLTR() const { return eBidiLTR == GetBidiDir(); }
203
204 /**
205 * Return true if bidi direction is RTL. (Convenience method)
206 */
IsBidiRTL()207 bool IsBidiRTL() const { return eBidiRTL == GetBidiDir(); }
208
209 /**
210 * True if it is vertical and vertical-lr, or is horizontal and bidi LTR.
211 */
IsPhysicalLTR()212 bool IsPhysicalLTR() const {
213 return IsVertical() ? IsVerticalLR() : IsBidiLTR();
214 }
215
216 /**
217 * True if it is vertical and vertical-rl, or is horizontal and bidi RTL.
218 */
IsPhysicalRTL()219 bool IsPhysicalRTL() const {
220 return IsVertical() ? IsVerticalRL() : IsBidiRTL();
221 }
222
223 /**
224 * True if vertical-mode block direction is LR (convenience method).
225 */
IsVerticalLR()226 bool IsVerticalLR() const { return eBlockLR == GetBlockDir(); }
227
228 /**
229 * True if vertical-mode block direction is RL (convenience method).
230 */
IsVerticalRL()231 bool IsVerticalRL() const { return eBlockRL == GetBlockDir(); }
232
233 /**
234 * True if vertical writing mode, i.e. when
235 * writing-mode: vertical-lr | vertical-rl.
236 */
IsVertical()237 bool IsVertical() const {
238 return !!(mWritingMode & StyleWritingMode::VERTICAL);
239 }
240
241 /**
242 * True if line-over/line-under are inverted from block-start/block-end.
243 * This is true only when writing-mode is vertical-lr.
244 */
IsLineInverted()245 bool IsLineInverted() const {
246 return !!(mWritingMode & StyleWritingMode::LINE_INVERTED);
247 }
248
249 /**
250 * Block-axis flow-relative to line-relative factor.
251 * May be used as a multiplication factor for block-axis coordinates
252 * to convert between flow- and line-relative coordinate systems (e.g.
253 * positioning an over- or under-line decoration).
254 */
FlowRelativeToLineRelativeFactor()255 int FlowRelativeToLineRelativeFactor() const {
256 return IsLineInverted() ? -1 : 1;
257 }
258
259 /**
260 * True if vertical sideways writing mode, i.e. when
261 * writing-mode: sideways-lr | sideways-rl.
262 */
IsVerticalSideways()263 bool IsVerticalSideways() const {
264 return !!(mWritingMode & StyleWritingMode::VERTICAL_SIDEWAYS);
265 }
266
267 /**
268 * True if this is writing-mode: sideways-rl (convenience method).
269 */
IsSidewaysRL()270 bool IsSidewaysRL() const { return IsVerticalRL() && IsVerticalSideways(); }
271
272 /**
273 * True if this is writing-mode: sideways-lr (convenience method).
274 */
IsSidewaysLR()275 bool IsSidewaysLR() const { return IsVerticalLR() && IsVerticalSideways(); }
276
277 /**
278 * True if either text-orientation or writing-mode will force all text to be
279 * rendered sideways in vertical lines, in which case we should prefer an
280 * alphabetic baseline; otherwise, the default is centered.
281 *
282 * Note that some glyph runs may be rendered sideways even if this is false,
283 * due to text-orientation:mixed resolution, but in that case the dominant
284 * baseline remains centered.
285 */
IsSideways()286 bool IsSideways() const {
287 return !!(mWritingMode & (StyleWritingMode::VERTICAL_SIDEWAYS |
288 StyleWritingMode::TEXT_SIDEWAYS));
289 }
290
291 #ifdef DEBUG
292 // Used by CHECK_WRITING_MODE to compare modes without regard for the
293 // StyleWritingMode::VERTICAL_SIDEWAYS or StyleWritingMode::TEXT_SIDEWAYS
294 // flags.
IgnoreSideways()295 WritingMode IgnoreSideways() const {
296 return WritingMode(
297 mWritingMode.bits &
298 ~(StyleWritingMode::VERTICAL_SIDEWAYS | StyleWritingMode::TEXT_SIDEWAYS)
299 .bits);
300 }
301 #endif
302
303 /**
304 * Return true if boxes with this writing mode should use central baselines.
305 */
IsCentralBaseline()306 bool IsCentralBaseline() const { return IsVertical() && !IsSideways(); }
307
308 /**
309 * Return true if boxes with this writing mode should use alphabetical
310 * baselines.
311 */
IsAlphabeticalBaseline()312 bool IsAlphabeticalBaseline() const { return !IsCentralBaseline(); }
313
PhysicalAxisForLogicalAxis(uint8_t aWritingModeValue,LogicalAxis aAxis)314 static mozilla::PhysicalAxis PhysicalAxisForLogicalAxis(
315 uint8_t aWritingModeValue, LogicalAxis aAxis) {
316 // This relies on bit 0 of a writing-value mode indicating vertical
317 // orientation and bit 0 of a LogicalAxis value indicating the inline axis,
318 // so that it can correctly form mozilla::PhysicalAxis values using bit
319 // manipulation.
320 static_assert(NS_STYLE_WRITING_MODE_HORIZONTAL_TB == 0 &&
321 NS_STYLE_WRITING_MODE_VERTICAL_RL == 1 &&
322 NS_STYLE_WRITING_MODE_VERTICAL_LR == 3 &&
323 eLogicalAxisBlock == 0 && eLogicalAxisInline == 1 &&
324 eAxisVertical == 0 && eAxisHorizontal == 1,
325 "unexpected writing-mode, logical axis or physical axis "
326 "constant values");
327 return mozilla::PhysicalAxis((aWritingModeValue ^ aAxis) & 0x1);
328 }
329
PhysicalAxis(LogicalAxis aAxis)330 mozilla::PhysicalAxis PhysicalAxis(LogicalAxis aAxis) const {
331 // This will set wm to either NS_STYLE_WRITING_MODE_HORIZONTAL_TB or
332 // NS_STYLE_WRITING_MODE_VERTICAL_RL, and not the other two (real
333 // and hypothetical) values. But this is fine; we only need to
334 // distinguish between vertical and horizontal in
335 // PhysicalAxisForLogicalAxis.
336 const auto wm = (mWritingMode & StyleWritingMode::VERTICAL).bits;
337 return PhysicalAxisForLogicalAxis(wm, aAxis);
338 }
339
PhysicalSideForBlockAxis(uint8_t aWritingModeValue,LogicalEdge aEdge)340 static mozilla::Side PhysicalSideForBlockAxis(uint8_t aWritingModeValue,
341 LogicalEdge aEdge) {
342 // indexes are NS_STYLE_WRITING_MODE_* values, which are the same as these
343 // two-bit values:
344 // bit 0 = the StyleWritingMode::VERTICAL value
345 // bit 1 = the StyleWritingMode::VERTICAL_LR value
346 static const mozilla::Side kLogicalBlockSides[][2] = {
347 {eSideTop, eSideBottom}, // horizontal-tb
348 {eSideRight, eSideLeft}, // vertical-rl
349 {eSideBottom, eSideTop}, // (horizontal-bt)
350 {eSideLeft, eSideRight}, // vertical-lr
351 };
352
353 // Ignore the SIDEWAYS_MASK bit of the writing-mode value, as this has no
354 // effect on the side mappings.
355 aWritingModeValue &= ~NS_STYLE_WRITING_MODE_SIDEWAYS_MASK;
356
357 // What's left of the writing-mode should be in the range 0-3:
358 NS_ASSERTION(aWritingModeValue < 4, "invalid aWritingModeValue value");
359
360 return kLogicalBlockSides[aWritingModeValue][aEdge];
361 }
362
PhysicalSideForInlineAxis(LogicalEdge aEdge)363 mozilla::Side PhysicalSideForInlineAxis(LogicalEdge aEdge) const {
364 // indexes are four-bit values:
365 // bit 0 = the StyleWritingMode::VERTICAL value
366 // bit 1 = the StyleWritingMode::INLINE_REVERSED value
367 // bit 2 = the StyleWritingMode::VERTICAL_LR value
368 // bit 3 = the StyleWritingMode::LINE_INVERTED value
369 // Not all of these combinations can actually be specified via CSS: there
370 // is no horizontal-bt writing-mode, and no text-orientation value that
371 // produces "inverted" text. (The former 'sideways-left' value, no longer
372 // in the spec, would have produced this in vertical-rl mode.)
373 static const mozilla::Side kLogicalInlineSides[][2] = {
374 {eSideLeft, eSideRight}, // horizontal-tb ltr
375 {eSideTop, eSideBottom}, // vertical-rl ltr
376 {eSideRight, eSideLeft}, // horizontal-tb rtl
377 {eSideBottom, eSideTop}, // vertical-rl rtl
378 {eSideRight, eSideLeft}, // (horizontal-bt) (inverted) ltr
379 {eSideTop, eSideBottom}, // sideways-lr rtl
380 {eSideLeft, eSideRight}, // (horizontal-bt) (inverted) rtl
381 {eSideBottom, eSideTop}, // sideways-lr ltr
382 {eSideLeft, eSideRight}, // horizontal-tb (inverted) rtl
383 {eSideTop, eSideBottom}, // vertical-rl (inverted) rtl
384 {eSideRight, eSideLeft}, // horizontal-tb (inverted) ltr
385 {eSideBottom, eSideTop}, // vertical-rl (inverted) ltr
386 {eSideLeft, eSideRight}, // (horizontal-bt) ltr
387 {eSideTop, eSideBottom}, // vertical-lr ltr
388 {eSideRight, eSideLeft}, // (horizontal-bt) rtl
389 {eSideBottom, eSideTop}, // vertical-lr rtl
390 };
391
392 // Inline axis sides depend on all three of writing-mode, text-orientation
393 // and direction, which are encoded in the StyleWritingMode::VERTICAL,
394 // StyleWritingMode::INLINE_REVERSED, StyleWritingMode::VERTICAL_LR and
395 // StyleWritingMode::LINE_INVERTED bits. Use these four bits to index into
396 // kLogicalInlineSides.
397 MOZ_ASSERT(StyleWritingMode::VERTICAL.bits == 0x01 &&
398 StyleWritingMode::INLINE_REVERSED.bits == 0x02 &&
399 StyleWritingMode::VERTICAL_LR.bits == 0x04 &&
400 StyleWritingMode::LINE_INVERTED.bits == 0x08,
401 "unexpected mask values");
402 int index = mWritingMode.bits & 0x0F;
403 return kLogicalInlineSides[index][aEdge];
404 }
405
406 /**
407 * Returns the physical side corresponding to the specified logical side,
408 * given the current writing mode.
409 */
PhysicalSide(LogicalSide aSide)410 mozilla::Side PhysicalSide(LogicalSide aSide) const {
411 if (IsBlock(aSide)) {
412 MOZ_ASSERT(StyleWritingMode::VERTICAL.bits == 0x01 &&
413 StyleWritingMode::VERTICAL_LR.bits == 0x04,
414 "unexpected mask values");
415 const uint8_t wm =
416 ((mWritingMode & StyleWritingMode::VERTICAL_LR).bits >> 1) |
417 (mWritingMode & StyleWritingMode::VERTICAL).bits;
418 return PhysicalSideForBlockAxis(wm, GetEdge(aSide));
419 }
420
421 return PhysicalSideForInlineAxis(GetEdge(aSide));
422 }
423
424 /**
425 * Returns the logical side corresponding to the specified physical side,
426 * given the current writing mode.
427 * (This is the inverse of the PhysicalSide() method above.)
428 */
LogicalSideForPhysicalSide(mozilla::Side aSide)429 LogicalSide LogicalSideForPhysicalSide(mozilla::Side aSide) const {
430 // clang-format off
431 // indexes are four-bit values:
432 // bit 0 = the StyleWritingMode::VERTICAL value
433 // bit 1 = the StyleWritingMode::INLINE_REVERSED value
434 // bit 2 = the StyleWritingMode::VERTICAL_LR value
435 // bit 3 = the StyleWritingMode::LINE_INVERTED value
436 static const LogicalSide kPhysicalToLogicalSides[][4] = {
437 // top right
438 // bottom left
439 { eLogicalSideBStart, eLogicalSideIEnd,
440 eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb ltr
441 { eLogicalSideIStart, eLogicalSideBStart,
442 eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl ltr
443 { eLogicalSideBStart, eLogicalSideIStart,
444 eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb rtl
445 { eLogicalSideIEnd, eLogicalSideBStart,
446 eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl rtl
447 { eLogicalSideBEnd, eLogicalSideIStart,
448 eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) (inv) ltr
449 { eLogicalSideIStart, eLogicalSideBEnd,
450 eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr sw-left rtl
451 { eLogicalSideBEnd, eLogicalSideIEnd,
452 eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) (inv) rtl
453 { eLogicalSideIEnd, eLogicalSideBEnd,
454 eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr sw-left ltr
455 { eLogicalSideBStart, eLogicalSideIEnd,
456 eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb (inv) rtl
457 { eLogicalSideIStart, eLogicalSideBStart,
458 eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl sw-left rtl
459 { eLogicalSideBStart, eLogicalSideIStart,
460 eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb (inv) ltr
461 { eLogicalSideIEnd, eLogicalSideBStart,
462 eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl sw-left ltr
463 { eLogicalSideBEnd, eLogicalSideIEnd,
464 eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) ltr
465 { eLogicalSideIStart, eLogicalSideBEnd,
466 eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr ltr
467 { eLogicalSideBEnd, eLogicalSideIStart,
468 eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) rtl
469 { eLogicalSideIEnd, eLogicalSideBEnd,
470 eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr rtl
471 };
472 // clang-format on
473
474 MOZ_ASSERT(StyleWritingMode::VERTICAL.bits == 0x01 &&
475 StyleWritingMode::INLINE_REVERSED.bits == 0x02 &&
476 StyleWritingMode::VERTICAL_LR.bits == 0x04 &&
477 StyleWritingMode::LINE_INVERTED.bits == 0x08,
478 "unexpected mask values");
479 int index = mWritingMode.bits & 0x0F;
480 return kPhysicalToLogicalSides[index][aSide];
481 }
482
483 /**
484 * Returns the logical side corresponding to the specified
485 * line-relative direction, given the current writing mode.
486 */
LogicalSideForLineRelativeDir(LineRelativeDir aDir)487 LogicalSide LogicalSideForLineRelativeDir(LineRelativeDir aDir) const {
488 auto side = static_cast<LogicalSide>(aDir);
489 if (IsInline(side)) {
490 return IsBidiLTR() ? side : GetOppositeSide(side);
491 }
492 return !IsLineInverted() ? side : GetOppositeSide(side);
493 }
494
495 /**
496 * Default constructor gives us a horizontal, LTR writing mode.
497 * XXX We will probably eliminate this and require explicit initialization
498 * in all cases once transition is complete.
499 */
WritingMode()500 WritingMode() : mWritingMode{0} {}
501
502 /**
503 * Construct writing mode based on a ComputedStyle.
504 */
WritingMode(const ComputedStyle * aComputedStyle)505 explicit WritingMode(const ComputedStyle* aComputedStyle) {
506 NS_ASSERTION(aComputedStyle, "we need an ComputedStyle here");
507 mWritingMode = aComputedStyle->WritingMode();
508 }
509
510 /**
511 * This function performs fixup for elements with 'unicode-bidi: plaintext',
512 * where inline directionality is derived from the Unicode bidi categories
513 * of the element's content, and not the CSS 'direction' property.
514 *
515 * The WritingMode constructor will have already incorporated the 'direction'
516 * property into our flag bits, so such elements need to use this method
517 * (after resolving the bidi level of their content) to update the direction
518 * bits as needed.
519 *
520 * If it turns out that our bidi direction already matches what plaintext
521 * resolution determined, there's nothing to do here. If it didn't (i.e. if
522 * the rtl-ness doesn't match), then we correct the direction by flipping the
523 * same bits that get flipped in the constructor's CSS 'direction'-based
524 * chunk.
525 *
526 * XXX change uint8_t to UBiDiLevel after bug 924851
527 */
SetDirectionFromBidiLevel(uint8_t level)528 void SetDirectionFromBidiLevel(uint8_t level) {
529 if (IS_LEVEL_RTL(level) == IsBidiLTR()) {
530 mWritingMode ^= StyleWritingMode::RTL | StyleWritingMode::INLINE_REVERSED;
531 }
532 }
533
534 /**
535 * Compare two WritingModes for equality.
536 */
537 bool operator==(const WritingMode& aOther) const {
538 return mWritingMode == aOther.mWritingMode;
539 }
540
541 bool operator!=(const WritingMode& aOther) const {
542 return mWritingMode != aOther.mWritingMode;
543 }
544
545 /**
546 * Check whether two modes are orthogonal to each other.
547 */
IsOrthogonalTo(const WritingMode & aOther)548 bool IsOrthogonalTo(const WritingMode& aOther) const {
549 return IsVertical() != aOther.IsVertical();
550 }
551
552 /**
553 * Returns true if this WritingMode's aLogicalAxis has the same physical
554 * start side as the parallel axis of WritingMode |aOther|.
555 *
556 * @param aLogicalAxis The axis to compare from this WritingMode.
557 * @param aOther The other WritingMode (from which we'll choose the axis
558 * that's parallel to this WritingMode's aLogicalAxis, for
559 * comparison).
560 */
ParallelAxisStartsOnSameSide(LogicalAxis aLogicalAxis,const WritingMode & aOther)561 bool ParallelAxisStartsOnSameSide(LogicalAxis aLogicalAxis,
562 const WritingMode& aOther) const {
563 mozilla::Side myStartSide =
564 this->PhysicalSide(MakeLogicalSide(aLogicalAxis, eLogicalEdgeStart));
565
566 // Figure out which of aOther's axes is parallel to |this| WritingMode's
567 // aLogicalAxis, and get its physical start side as well.
568 LogicalAxis otherWMAxis = aOther.IsOrthogonalTo(*this)
569 ? GetOrthogonalAxis(aLogicalAxis)
570 : aLogicalAxis;
571 mozilla::Side otherWMStartSide =
572 aOther.PhysicalSide(MakeLogicalSide(otherWMAxis, eLogicalEdgeStart));
573
574 NS_ASSERTION(myStartSide % 2 == otherWMStartSide % 2,
575 "Should end up with sides in the same physical axis");
576 return myStartSide == otherWMStartSide;
577 }
578
GetBits()579 uint8_t GetBits() const { return mWritingMode.bits; }
580
581 private:
582 friend class LogicalPoint;
583 friend class LogicalSize;
584 friend struct LogicalSides;
585 friend class LogicalMargin;
586 friend class LogicalRect;
587
588 friend struct IPC::ParamTraits<WritingMode>;
589 // IMENotification cannot store this class directly since this has some
590 // constructors. Therefore, it stores mWritingMode and recreate the
591 // instance from it.
592 friend struct widget::IMENotification;
593
594 /**
595 * Return a WritingMode representing an unknown value.
596 */
597 static inline WritingMode Unknown() {
598 return WritingMode(eUnknownWritingMode);
599 }
600
601 /**
602 * Constructing a WritingMode with an arbitrary value is a private operation
603 * currently only used by the Unknown() and IgnoreSideways() methods.
604 */
605 explicit WritingMode(uint8_t aValue) : mWritingMode{aValue} {}
606
607 StyleWritingMode mWritingMode;
608
609 enum Masks {
610 // Masks for output enums
611 eInlineMask = 0x03, // VERTICAL | INLINE_REVERSED
612 eBlockMask = 0x05, // VERTICAL | VERTICAL_LR
613 };
614 };
615
616 inline std::ostream& operator<<(std::ostream& aStream, const WritingMode& aWM) {
617 return aStream
618 << (aWM.IsVertical()
619 ? aWM.IsVerticalLR()
620 ? aWM.IsBidiLTR()
621 ? aWM.IsSideways() ? "sw-lr-ltr" : "v-lr-ltr"
622 : aWM.IsSideways() ? "sw-lr-rtl" : "v-lr-rtl"
623 : aWM.IsBidiLTR()
624 ? aWM.IsSideways() ? "sw-rl-ltr" : "v-rl-ltr"
625 : aWM.IsSideways() ? "sw-rl-rtl" : "v-rl-rtl"
626 : aWM.IsBidiLTR() ? "h-ltr" : "h-rtl");
627 }
628
629 /**
630 * Logical-coordinate classes:
631 *
632 * There are three sets of coordinate space:
633 * - physical (top, left, bottom, right)
634 * relative to graphics coord system
635 * - flow-relative (block-start, inline-start, block-end, inline-end)
636 * relative to block/inline flow directions
637 * - line-relative (line-over, line-left, line-under, line-right)
638 * relative to glyph orientation / inline bidi directions
639 * See CSS3 Writing Modes for more information
640 * http://www.w3.org/TR/css3-writing-modes/#abstract-box
641 *
642 * For shorthand, B represents the block-axis
643 * I represents the inline-axis
644 *
645 * The flow-relative geometric classes store coords in flow-relative space.
646 * They use a private ns{Point,Size,Rect,Margin} member to store the actual
647 * coordinate values, but reinterpret them as logical instead of physical.
648 * This allows us to easily perform calculations in logical space (provided
649 * writing modes of the operands match), by simply mapping to nsPoint (etc)
650 * methods.
651 *
652 * Physical-coordinate accessors/setters are responsible to translate these
653 * internal logical values as necessary.
654 *
655 * In DEBUG builds, the logical types store their WritingMode and check
656 * that the same WritingMode is passed whenever callers ask them to do a
657 * writing-mode-dependent operation. Non-DEBUG builds do NOT check this,
658 * to avoid the overhead of storing WritingMode fields.
659 *
660 * Open question: do we need a different set optimized for line-relative
661 * math, for use in nsLineLayout and the like? Or is multiplying values
662 * by FlowRelativeToLineRelativeFactor() enough?
663 */
664
665 /**
666 * Flow-relative point
667 */
668 class LogicalPoint {
669 public:
670 explicit LogicalPoint(WritingMode aWritingMode)
671 :
672 #ifdef DEBUG
673 mWritingMode(aWritingMode),
674 #endif
675 mPoint(0, 0) {
676 }
677
678 // Construct from a writing mode and individual coordinates (which MUST be
679 // values in that writing mode, NOT physical coordinates!)
680 LogicalPoint(WritingMode aWritingMode, nscoord aI, nscoord aB)
681 :
682 #ifdef DEBUG
683 mWritingMode(aWritingMode),
684 #endif
685 mPoint(aI, aB) {
686 }
687
688 // Construct from a writing mode and a physical point, within a given
689 // containing rectangle's size (defining the conversion between LTR
690 // and RTL coordinates, and between TTB and BTT coordinates).
691 LogicalPoint(WritingMode aWritingMode, const nsPoint& aPoint,
692 const nsSize& aContainerSize)
693 #ifdef DEBUG
694 : mWritingMode(aWritingMode)
695 #endif
696 {
697 if (aWritingMode.IsVertical()) {
698 I() = aWritingMode.IsInlineReversed() ? aContainerSize.height - aPoint.y
699 : aPoint.y;
700 B() = aWritingMode.IsVerticalLR() ? aPoint.x
701 : aContainerSize.width - aPoint.x;
702 } else {
703 I() = aWritingMode.IsInlineReversed() ? aContainerSize.width - aPoint.x
704 : aPoint.x;
705 B() = aPoint.y;
706 }
707 }
708
709 /**
710 * Read-only (const) access to the logical coordinates.
711 */
712 nscoord I(WritingMode aWritingMode) const // inline-axis
713 {
714 CHECK_WRITING_MODE(aWritingMode);
715 return mPoint.x;
716 }
717 nscoord B(WritingMode aWritingMode) const // block-axis
718 {
719 CHECK_WRITING_MODE(aWritingMode);
720 return mPoint.y;
721 }
722 nscoord Pos(LogicalAxis aAxis, WritingMode aWM) const {
723 return aAxis == eLogicalAxisInline ? I(aWM) : B(aWM);
724 }
725 nscoord LineRelative(WritingMode aWritingMode,
726 const nsSize& aContainerSize) const // line-axis
727 {
728 CHECK_WRITING_MODE(aWritingMode);
729 if (aWritingMode.IsBidiLTR()) {
730 return I();
731 }
732 return (aWritingMode.IsVertical() ? aContainerSize.height
733 : aContainerSize.width) -
734 I();
735 }
736
737 /**
738 * These non-const accessors return a reference (lvalue) that can be
739 * assigned to by callers.
740 */
741 nscoord& I(WritingMode aWritingMode) // inline-axis
742 {
743 CHECK_WRITING_MODE(aWritingMode);
744 return mPoint.x;
745 }
746 nscoord& B(WritingMode aWritingMode) // block-axis
747 {
748 CHECK_WRITING_MODE(aWritingMode);
749 return mPoint.y;
750 }
751 nscoord& Pos(LogicalAxis aAxis, WritingMode aWM) {
752 return aAxis == eLogicalAxisInline ? I(aWM) : B(aWM);
753 }
754
755 /**
756 * Return a physical point corresponding to our logical coordinates,
757 * converted according to our writing mode.
758 */
759 nsPoint GetPhysicalPoint(WritingMode aWritingMode,
760 const nsSize& aContainerSize) const {
761 CHECK_WRITING_MODE(aWritingMode);
762 if (aWritingMode.IsVertical()) {
763 return nsPoint(
764 aWritingMode.IsVerticalLR() ? B() : aContainerSize.width - B(),
765 aWritingMode.IsInlineReversed() ? aContainerSize.height - I() : I());
766 } else {
767 return nsPoint(
768 aWritingMode.IsInlineReversed() ? aContainerSize.width - I() : I(),
769 B());
770 }
771 }
772
773 /**
774 * Return the equivalent point in a different writing mode.
775 */
776 LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
777 const nsSize& aContainerSize) const {
778 CHECK_WRITING_MODE(aFromMode);
779 return aToMode == aFromMode
780 ? *this
781 : LogicalPoint(aToMode,
782 GetPhysicalPoint(aFromMode, aContainerSize),
783 aContainerSize);
784 }
785
786 bool operator==(const LogicalPoint& aOther) const {
787 CHECK_WRITING_MODE(aOther.GetWritingMode());
788 return mPoint == aOther.mPoint;
789 }
790
791 bool operator!=(const LogicalPoint& aOther) const {
792 CHECK_WRITING_MODE(aOther.GetWritingMode());
793 return mPoint != aOther.mPoint;
794 }
795
796 LogicalPoint operator+(const LogicalPoint& aOther) const {
797 CHECK_WRITING_MODE(aOther.GetWritingMode());
798 // In non-debug builds, LogicalPoint does not store the WritingMode,
799 // so the first parameter here (which will always be eUnknownWritingMode)
800 // is ignored.
801 return LogicalPoint(GetWritingMode(), mPoint.x + aOther.mPoint.x,
802 mPoint.y + aOther.mPoint.y);
803 }
804
805 LogicalPoint& operator+=(const LogicalPoint& aOther) {
806 CHECK_WRITING_MODE(aOther.GetWritingMode());
807 I() += aOther.I();
808 B() += aOther.B();
809 return *this;
810 }
811
812 LogicalPoint operator-(const LogicalPoint& aOther) const {
813 CHECK_WRITING_MODE(aOther.GetWritingMode());
814 // In non-debug builds, LogicalPoint does not store the WritingMode,
815 // so the first parameter here (which will always be eUnknownWritingMode)
816 // is ignored.
817 return LogicalPoint(GetWritingMode(), mPoint.x - aOther.mPoint.x,
818 mPoint.y - aOther.mPoint.y);
819 }
820
821 LogicalPoint& operator-=(const LogicalPoint& aOther) {
822 CHECK_WRITING_MODE(aOther.GetWritingMode());
823 I() -= aOther.I();
824 B() -= aOther.B();
825 return *this;
826 }
827
828 friend std::ostream& operator<<(std::ostream& aStream,
829 const LogicalPoint& aPoint) {
830 return aStream << aPoint.mPoint;
831 }
832
833 private:
834 friend class LogicalRect;
835
836 /**
837 * NOTE that in non-DEBUG builds, GetWritingMode() always returns
838 * eUnknownWritingMode, as the current mode is not stored in the logical-
839 * geometry classes. Therefore, this method is private; it is used ONLY
840 * by the DEBUG-mode checking macros in this class and its friends;
841 * other code is not allowed to ask a logical point for its writing mode,
842 * as this info will simply not be available in non-DEBUG builds.
843 *
844 * Also, in non-DEBUG builds, CHECK_WRITING_MODE does nothing, and the
845 * WritingMode parameter to logical methods will generally be optimized
846 * away altogether.
847 */
848 #ifdef DEBUG
849 WritingMode GetWritingMode() const { return mWritingMode; }
850 #else
851 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
852 #endif
853
854 // We don't allow construction of a LogicalPoint with no writing mode.
855 LogicalPoint() = delete;
856
857 // Accessors that don't take or check a WritingMode value.
858 // These are for internal use only; they are called by methods that have
859 // themselves already checked the WritingMode passed by the caller.
860 nscoord I() const // inline-axis
861 {
862 return mPoint.x;
863 }
864 nscoord B() const // block-axis
865 {
866 return mPoint.y;
867 }
868
869 nscoord& I() // inline-axis
870 {
871 return mPoint.x;
872 }
873 nscoord& B() // block-axis
874 {
875 return mPoint.y;
876 }
877
878 #ifdef DEBUG
879 WritingMode mWritingMode;
880 #endif
881
882 // We use an nsPoint to hold the coordinates, but reinterpret its .x and .y
883 // fields as the inline and block directions. Hence, this is not exposed
884 // directly, but only through accessors that will map them according to the
885 // writing mode.
886 nsPoint mPoint;
887 };
888
889 /**
890 * Flow-relative size
891 */
892 class LogicalSize {
893 public:
894 explicit LogicalSize(WritingMode aWritingMode)
895 :
896 #ifdef DEBUG
897 mWritingMode(aWritingMode),
898 #endif
899 mSize(0, 0) {
900 }
901
902 LogicalSize(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
903 :
904 #ifdef DEBUG
905 mWritingMode(aWritingMode),
906 #endif
907 mSize(aISize, aBSize) {
908 }
909
910 LogicalSize(WritingMode aWritingMode, const nsSize& aPhysicalSize)
911 #ifdef DEBUG
912 : mWritingMode(aWritingMode)
913 #endif
914 {
915 if (aWritingMode.IsVertical()) {
916 ISize() = aPhysicalSize.height;
917 BSize() = aPhysicalSize.width;
918 } else {
919 ISize() = aPhysicalSize.width;
920 BSize() = aPhysicalSize.height;
921 }
922 }
923
924 void SizeTo(WritingMode aWritingMode, nscoord aISize, nscoord aBSize) {
925 CHECK_WRITING_MODE(aWritingMode);
926 mSize.SizeTo(aISize, aBSize);
927 }
928
929 /**
930 * Dimensions in logical and physical terms
931 */
932 nscoord ISize(WritingMode aWritingMode) const // inline-size
933 {
934 CHECK_WRITING_MODE(aWritingMode);
935 return mSize.width;
936 }
937 nscoord BSize(WritingMode aWritingMode) const // block-size
938 {
939 CHECK_WRITING_MODE(aWritingMode);
940 return mSize.height;
941 }
942 nscoord Size(LogicalAxis aAxis, WritingMode aWM) const {
943 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
944 }
945
946 nscoord Width(WritingMode aWritingMode) const {
947 CHECK_WRITING_MODE(aWritingMode);
948 return aWritingMode.IsVertical() ? BSize() : ISize();
949 }
950 nscoord Height(WritingMode aWritingMode) const {
951 CHECK_WRITING_MODE(aWritingMode);
952 return aWritingMode.IsVertical() ? ISize() : BSize();
953 }
954
955 /**
956 * Writable references to the logical dimensions
957 */
958 nscoord& ISize(WritingMode aWritingMode) // inline-size
959 {
960 CHECK_WRITING_MODE(aWritingMode);
961 return mSize.width;
962 }
963 nscoord& BSize(WritingMode aWritingMode) // block-size
964 {
965 CHECK_WRITING_MODE(aWritingMode);
966 return mSize.height;
967 }
968 nscoord& Size(LogicalAxis aAxis, WritingMode aWM) {
969 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
970 }
971
972 /**
973 * Return an nsSize containing our physical dimensions
974 */
975 nsSize GetPhysicalSize(WritingMode aWritingMode) const {
976 CHECK_WRITING_MODE(aWritingMode);
977 return aWritingMode.IsVertical() ? nsSize(BSize(), ISize())
978 : nsSize(ISize(), BSize());
979 }
980
981 /**
982 * Return a LogicalSize representing this size in a different writing mode
983 */
984 LogicalSize ConvertTo(WritingMode aToMode, WritingMode aFromMode) const {
985 #ifdef DEBUG
986 // In DEBUG builds make sure to return a LogicalSize with the
987 // expected writing mode
988 CHECK_WRITING_MODE(aFromMode);
989 return aToMode == aFromMode
990 ? *this
991 : LogicalSize(aToMode, GetPhysicalSize(aFromMode));
992 #else
993 // optimization for non-DEBUG builds where LogicalSize doesn't store
994 // the writing mode
995 return (aToMode == aFromMode || !aToMode.IsOrthogonalTo(aFromMode))
996 ? *this
997 : LogicalSize(aToMode, BSize(), ISize());
998 #endif
999 }
1000
1001 /**
1002 * Test if a size is (0, 0).
1003 */
1004 bool IsAllZero() const { return ISize() == 0 && BSize() == 0; }
1005
1006 /**
1007 * Various binary operators on LogicalSize. These are valid ONLY for operands
1008 * that share the same writing mode.
1009 */
1010 bool operator==(const LogicalSize& aOther) const {
1011 CHECK_WRITING_MODE(aOther.GetWritingMode());
1012 return mSize == aOther.mSize;
1013 }
1014
1015 bool operator!=(const LogicalSize& aOther) const {
1016 CHECK_WRITING_MODE(aOther.GetWritingMode());
1017 return mSize != aOther.mSize;
1018 }
1019
1020 LogicalSize operator+(const LogicalSize& aOther) const {
1021 CHECK_WRITING_MODE(aOther.GetWritingMode());
1022 return LogicalSize(GetWritingMode(), ISize() + aOther.ISize(),
1023 BSize() + aOther.BSize());
1024 }
1025 LogicalSize& operator+=(const LogicalSize& aOther) {
1026 CHECK_WRITING_MODE(aOther.GetWritingMode());
1027 ISize() += aOther.ISize();
1028 BSize() += aOther.BSize();
1029 return *this;
1030 }
1031
1032 LogicalSize operator-(const LogicalSize& aOther) const {
1033 CHECK_WRITING_MODE(aOther.GetWritingMode());
1034 return LogicalSize(GetWritingMode(), ISize() - aOther.ISize(),
1035 BSize() - aOther.BSize());
1036 }
1037 LogicalSize& operator-=(const LogicalSize& aOther) {
1038 CHECK_WRITING_MODE(aOther.GetWritingMode());
1039 ISize() -= aOther.ISize();
1040 BSize() -= aOther.BSize();
1041 return *this;
1042 }
1043
1044 friend std::ostream& operator<<(std::ostream& aStream,
1045 const LogicalSize& aSize) {
1046 return aStream << aSize.mSize;
1047 }
1048
1049 private:
1050 friend class LogicalRect;
1051
1052 LogicalSize() = delete;
1053
1054 #ifdef DEBUG
1055 WritingMode GetWritingMode() const { return mWritingMode; }
1056 #else
1057 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1058 #endif
1059
1060 nscoord ISize() const // inline-size
1061 {
1062 return mSize.width;
1063 }
1064 nscoord BSize() const // block-size
1065 {
1066 return mSize.height;
1067 }
1068
1069 nscoord& ISize() // inline-size
1070 {
1071 return mSize.width;
1072 }
1073 nscoord& BSize() // block-size
1074 {
1075 return mSize.height;
1076 }
1077
1078 #ifdef DEBUG
1079 WritingMode mWritingMode;
1080 #endif
1081 nsSize mSize;
1082 };
1083
1084 /**
1085 * LogicalSides represents a set of logical sides.
1086 */
1087 struct LogicalSides final {
1088 explicit LogicalSides(WritingMode aWritingMode)
1089 :
1090 #ifdef DEBUG
1091 mWritingMode(aWritingMode),
1092 #endif
1093 mBits(0) {
1094 }
1095 LogicalSides(WritingMode aWritingMode, LogicalSideBits aSideBits)
1096 :
1097 #ifdef DEBUG
1098 mWritingMode(aWritingMode),
1099 #endif
1100 mBits(aSideBits) {
1101 MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
1102 }
1103 bool IsEmpty() const { return mBits == 0; }
1104 bool BStart() const { return mBits & eLogicalSideBitsBStart; }
1105 bool BEnd() const { return mBits & eLogicalSideBitsBEnd; }
1106 bool IStart() const { return mBits & eLogicalSideBitsIStart; }
1107 bool IEnd() const { return mBits & eLogicalSideBitsIEnd; }
1108 bool Contains(LogicalSideBits aSideBits) const {
1109 MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
1110 return (mBits & aSideBits) == aSideBits;
1111 }
1112 LogicalSides operator|(LogicalSides aOther) const {
1113 CHECK_WRITING_MODE(aOther.GetWritingMode());
1114 return *this | LogicalSideBits(aOther.mBits);
1115 }
1116 LogicalSides operator|(LogicalSideBits aSideBits) const {
1117 return LogicalSides(GetWritingMode(), LogicalSideBits(mBits | aSideBits));
1118 }
1119 LogicalSides& operator|=(LogicalSides aOther) {
1120 CHECK_WRITING_MODE(aOther.GetWritingMode());
1121 return *this |= LogicalSideBits(aOther.mBits);
1122 }
1123 LogicalSides& operator|=(LogicalSideBits aSideBits) {
1124 mBits |= aSideBits;
1125 return *this;
1126 }
1127 bool operator==(LogicalSides aOther) const {
1128 CHECK_WRITING_MODE(aOther.GetWritingMode());
1129 return mBits == aOther.mBits;
1130 }
1131 bool operator!=(LogicalSides aOther) const {
1132 CHECK_WRITING_MODE(aOther.GetWritingMode());
1133 return !(*this == aOther);
1134 }
1135
1136 #ifdef DEBUG
1137 WritingMode GetWritingMode() const { return mWritingMode; }
1138 #else
1139 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1140 #endif
1141
1142 private:
1143 #ifdef DEBUG
1144 WritingMode mWritingMode;
1145 #endif
1146 uint8_t mBits;
1147 };
1148
1149 /**
1150 * Flow-relative margin
1151 */
1152 class LogicalMargin {
1153 public:
1154 explicit LogicalMargin(WritingMode aWritingMode)
1155 :
1156 #ifdef DEBUG
1157 mWritingMode(aWritingMode),
1158 #endif
1159 mMargin(0, 0, 0, 0) {
1160 }
1161
1162 LogicalMargin(WritingMode aWritingMode, nscoord aBStart, nscoord aIEnd,
1163 nscoord aBEnd, nscoord aIStart)
1164 :
1165 #ifdef DEBUG
1166 mWritingMode(aWritingMode),
1167 #endif
1168 mMargin(aBStart, aIEnd, aBEnd, aIStart) {
1169 }
1170
1171 LogicalMargin(WritingMode aWritingMode, const nsMargin& aPhysicalMargin)
1172 #ifdef DEBUG
1173 : mWritingMode(aWritingMode)
1174 #endif
1175 {
1176 if (aWritingMode.IsVertical()) {
1177 if (aWritingMode.IsVerticalLR()) {
1178 mMargin.top = aPhysicalMargin.left;
1179 mMargin.bottom = aPhysicalMargin.right;
1180 } else {
1181 mMargin.top = aPhysicalMargin.right;
1182 mMargin.bottom = aPhysicalMargin.left;
1183 }
1184 if (aWritingMode.IsInlineReversed()) {
1185 mMargin.left = aPhysicalMargin.bottom;
1186 mMargin.right = aPhysicalMargin.top;
1187 } else {
1188 mMargin.left = aPhysicalMargin.top;
1189 mMargin.right = aPhysicalMargin.bottom;
1190 }
1191 } else {
1192 mMargin.top = aPhysicalMargin.top;
1193 mMargin.bottom = aPhysicalMargin.bottom;
1194 if (aWritingMode.IsInlineReversed()) {
1195 mMargin.left = aPhysicalMargin.right;
1196 mMargin.right = aPhysicalMargin.left;
1197 } else {
1198 mMargin.left = aPhysicalMargin.left;
1199 mMargin.right = aPhysicalMargin.right;
1200 }
1201 }
1202 }
1203
1204 nscoord IStart(WritingMode aWritingMode) const // inline-start margin
1205 {
1206 CHECK_WRITING_MODE(aWritingMode);
1207 return mMargin.left;
1208 }
1209 nscoord IEnd(WritingMode aWritingMode) const // inline-end margin
1210 {
1211 CHECK_WRITING_MODE(aWritingMode);
1212 return mMargin.right;
1213 }
1214 nscoord BStart(WritingMode aWritingMode) const // block-start margin
1215 {
1216 CHECK_WRITING_MODE(aWritingMode);
1217 return mMargin.top;
1218 }
1219 nscoord BEnd(WritingMode aWritingMode) const // block-end margin
1220 {
1221 CHECK_WRITING_MODE(aWritingMode);
1222 return mMargin.bottom;
1223 }
1224 nscoord Start(LogicalAxis aAxis, WritingMode aWM) const {
1225 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1226 }
1227 nscoord End(LogicalAxis aAxis, WritingMode aWM) const {
1228 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1229 }
1230
1231 nscoord& IStart(WritingMode aWritingMode) // inline-start margin
1232 {
1233 CHECK_WRITING_MODE(aWritingMode);
1234 return mMargin.left;
1235 }
1236 nscoord& IEnd(WritingMode aWritingMode) // inline-end margin
1237 {
1238 CHECK_WRITING_MODE(aWritingMode);
1239 return mMargin.right;
1240 }
1241 nscoord& BStart(WritingMode aWritingMode) // block-start margin
1242 {
1243 CHECK_WRITING_MODE(aWritingMode);
1244 return mMargin.top;
1245 }
1246 nscoord& BEnd(WritingMode aWritingMode) // block-end margin
1247 {
1248 CHECK_WRITING_MODE(aWritingMode);
1249 return mMargin.bottom;
1250 }
1251 nscoord& Start(LogicalAxis aAxis, WritingMode aWM) {
1252 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1253 }
1254 nscoord& End(LogicalAxis aAxis, WritingMode aWM) {
1255 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1256 }
1257
1258 nscoord IStartEnd(WritingMode aWritingMode) const // inline margins
1259 {
1260 CHECK_WRITING_MODE(aWritingMode);
1261 return mMargin.LeftRight();
1262 }
1263 nscoord BStartEnd(WritingMode aWritingMode) const // block margins
1264 {
1265 CHECK_WRITING_MODE(aWritingMode);
1266 return mMargin.TopBottom();
1267 }
1268 nscoord StartEnd(LogicalAxis aAxis, WritingMode aWM) const {
1269 return aAxis == eLogicalAxisInline ? IStartEnd(aWM) : BStartEnd(aWM);
1270 }
1271
1272 nscoord Side(LogicalSide aSide, WritingMode aWM) const {
1273 switch (aSide) {
1274 case eLogicalSideBStart:
1275 return BStart(aWM);
1276 case eLogicalSideBEnd:
1277 return BEnd(aWM);
1278 case eLogicalSideIStart:
1279 return IStart(aWM);
1280 case eLogicalSideIEnd:
1281 return IEnd(aWM);
1282 }
1283
1284 MOZ_ASSERT_UNREACHABLE("We should handle all sides!");
1285 return BStart(aWM);
1286 }
1287 nscoord& Side(LogicalSide aSide, WritingMode aWM) {
1288 switch (aSide) {
1289 case eLogicalSideBStart:
1290 return BStart(aWM);
1291 case eLogicalSideBEnd:
1292 return BEnd(aWM);
1293 case eLogicalSideIStart:
1294 return IStart(aWM);
1295 case eLogicalSideIEnd:
1296 return IEnd(aWM);
1297 }
1298
1299 MOZ_ASSERT_UNREACHABLE("We should handle all sides!");
1300 return BStart(aWM);
1301 }
1302
1303 /*
1304 * Return margin values for line-relative sides, as defined in
1305 * http://www.w3.org/TR/css-writing-modes-3/#line-directions:
1306 *
1307 * line-left
1308 * Nominally the side from which LTR text would start.
1309 * line-right
1310 * Nominally the side from which RTL text would start. (Opposite of
1311 * line-left.)
1312 */
1313 nscoord LineLeft(WritingMode aWritingMode) const {
1314 // We don't need to CHECK_WRITING_MODE here because the IStart or IEnd
1315 // accessor that we call will do it.
1316 return aWritingMode.IsBidiLTR() ? IStart(aWritingMode) : IEnd(aWritingMode);
1317 }
1318 nscoord LineRight(WritingMode aWritingMode) const {
1319 return aWritingMode.IsBidiLTR() ? IEnd(aWritingMode) : IStart(aWritingMode);
1320 }
1321
1322 /**
1323 * Return a LogicalSize representing the total size of the inline-
1324 * and block-dimension margins.
1325 */
1326 LogicalSize Size(WritingMode aWritingMode) const {
1327 CHECK_WRITING_MODE(aWritingMode);
1328 return LogicalSize(aWritingMode, IStartEnd(), BStartEnd());
1329 }
1330
1331 /**
1332 * Accessors for physical margins, using our writing mode to convert from
1333 * logical values.
1334 */
1335 nscoord Top(WritingMode aWritingMode) const {
1336 CHECK_WRITING_MODE(aWritingMode);
1337 return aWritingMode.IsVertical()
1338 ? (aWritingMode.IsInlineReversed() ? IEnd() : IStart())
1339 : BStart();
1340 }
1341
1342 nscoord Bottom(WritingMode aWritingMode) const {
1343 CHECK_WRITING_MODE(aWritingMode);
1344 return aWritingMode.IsVertical()
1345 ? (aWritingMode.IsInlineReversed() ? IStart() : IEnd())
1346 : BEnd();
1347 }
1348
1349 nscoord Left(WritingMode aWritingMode) const {
1350 CHECK_WRITING_MODE(aWritingMode);
1351 return aWritingMode.IsVertical()
1352 ? (aWritingMode.IsVerticalLR() ? BStart() : BEnd())
1353 : (aWritingMode.IsInlineReversed() ? IEnd() : IStart());
1354 }
1355
1356 nscoord Right(WritingMode aWritingMode) const {
1357 CHECK_WRITING_MODE(aWritingMode);
1358 return aWritingMode.IsVertical()
1359 ? (aWritingMode.IsVerticalLR() ? BEnd() : BStart())
1360 : (aWritingMode.IsInlineReversed() ? IStart() : IEnd());
1361 }
1362
1363 nscoord LeftRight(WritingMode aWritingMode) const {
1364 CHECK_WRITING_MODE(aWritingMode);
1365 return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
1366 }
1367
1368 nscoord TopBottom(WritingMode aWritingMode) const {
1369 CHECK_WRITING_MODE(aWritingMode);
1370 return aWritingMode.IsVertical() ? IStartEnd() : BStartEnd();
1371 }
1372
1373 void SizeTo(WritingMode aWritingMode, nscoord aBStart, nscoord aIEnd,
1374 nscoord aBEnd, nscoord aIStart) {
1375 CHECK_WRITING_MODE(aWritingMode);
1376 mMargin.SizeTo(aBStart, aIEnd, aBEnd, aIStart);
1377 }
1378
1379 /**
1380 * Return an nsMargin containing our physical coordinates
1381 */
1382 nsMargin GetPhysicalMargin(WritingMode aWritingMode) const {
1383 CHECK_WRITING_MODE(aWritingMode);
1384 return aWritingMode.IsVertical()
1385 ? (aWritingMode.IsVerticalLR()
1386 ? (aWritingMode.IsInlineReversed()
1387 ? nsMargin(IEnd(), BEnd(), IStart(), BStart())
1388 : nsMargin(IStart(), BEnd(), IEnd(), BStart()))
1389 : (aWritingMode.IsInlineReversed()
1390 ? nsMargin(IEnd(), BStart(), IStart(), BEnd())
1391 : nsMargin(IStart(), BStart(), IEnd(), BEnd())))
1392 : (aWritingMode.IsInlineReversed()
1393 ? nsMargin(BStart(), IStart(), BEnd(), IEnd())
1394 : nsMargin(BStart(), IEnd(), BEnd(), IStart()));
1395 }
1396
1397 /**
1398 * Return a LogicalMargin representing this margin in a different
1399 * writing mode
1400 */
1401 LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const {
1402 CHECK_WRITING_MODE(aFromMode);
1403 return aToMode == aFromMode
1404 ? *this
1405 : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
1406 }
1407
1408 LogicalMargin& ApplySkipSides(LogicalSides aSkipSides) {
1409 CHECK_WRITING_MODE(aSkipSides.GetWritingMode());
1410 if (aSkipSides.BStart()) {
1411 BStart() = 0;
1412 }
1413 if (aSkipSides.BEnd()) {
1414 BEnd() = 0;
1415 }
1416 if (aSkipSides.IStart()) {
1417 IStart() = 0;
1418 }
1419 if (aSkipSides.IEnd()) {
1420 IEnd() = 0;
1421 }
1422 return *this;
1423 }
1424
1425 bool IsAllZero() const {
1426 return (mMargin.left == 0 && mMargin.top == 0 && mMargin.right == 0 &&
1427 mMargin.bottom == 0);
1428 }
1429
1430 LogicalMargin operator+(const LogicalMargin& aMargin) const {
1431 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1432 return LogicalMargin(GetWritingMode(), BStart() + aMargin.BStart(),
1433 IEnd() + aMargin.IEnd(), BEnd() + aMargin.BEnd(),
1434 IStart() + aMargin.IStart());
1435 }
1436
1437 LogicalMargin operator+=(const LogicalMargin& aMargin) {
1438 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1439 mMargin += aMargin.mMargin;
1440 return *this;
1441 }
1442
1443 LogicalMargin operator-(const LogicalMargin& aMargin) const {
1444 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1445 return LogicalMargin(GetWritingMode(), BStart() - aMargin.BStart(),
1446 IEnd() - aMargin.IEnd(), BEnd() - aMargin.BEnd(),
1447 IStart() - aMargin.IStart());
1448 }
1449
1450 friend std::ostream& operator<<(std::ostream& aStream,
1451 const LogicalMargin& aMargin) {
1452 return aStream << aMargin.mMargin;
1453 }
1454
1455 private:
1456 friend class LogicalRect;
1457
1458 LogicalMargin() = delete;
1459
1460 #ifdef DEBUG
1461 WritingMode GetWritingMode() const { return mWritingMode; }
1462 #else
1463 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1464 #endif
1465
1466 nscoord IStart() const // inline-start margin
1467 {
1468 return mMargin.left;
1469 }
1470 nscoord IEnd() const // inline-end margin
1471 {
1472 return mMargin.right;
1473 }
1474 nscoord BStart() const // block-start margin
1475 {
1476 return mMargin.top;
1477 }
1478 nscoord BEnd() const // block-end margin
1479 {
1480 return mMargin.bottom;
1481 }
1482
1483 nscoord& IStart() // inline-start margin
1484 {
1485 return mMargin.left;
1486 }
1487 nscoord& IEnd() // inline-end margin
1488 {
1489 return mMargin.right;
1490 }
1491 nscoord& BStart() // block-start margin
1492 {
1493 return mMargin.top;
1494 }
1495 nscoord& BEnd() // block-end margin
1496 {
1497 return mMargin.bottom;
1498 }
1499
1500 nscoord IStartEnd() const // inline margins
1501 {
1502 return mMargin.LeftRight();
1503 }
1504 nscoord BStartEnd() const // block margins
1505 {
1506 return mMargin.TopBottom();
1507 }
1508
1509 #ifdef DEBUG
1510 WritingMode mWritingMode;
1511 #endif
1512 nsMargin mMargin;
1513 };
1514
1515 /**
1516 * Flow-relative rectangle
1517 */
1518 class LogicalRect {
1519 public:
1520 explicit LogicalRect(WritingMode aWritingMode)
1521 :
1522 #ifdef DEBUG
1523 mWritingMode(aWritingMode),
1524 #endif
1525 mIStart(0),
1526 mBStart(0),
1527 mISize(0),
1528 mBSize(0) {
1529 }
1530
1531 LogicalRect(WritingMode aWritingMode, nscoord aIStart, nscoord aBStart,
1532 nscoord aISize, nscoord aBSize)
1533 :
1534 #ifdef DEBUG
1535 mWritingMode(aWritingMode),
1536 #endif
1537 mIStart(aIStart),
1538 mBStart(aBStart),
1539 mISize(aISize),
1540 mBSize(aBSize) {
1541 }
1542
1543 LogicalRect(WritingMode aWritingMode, const LogicalPoint& aOrigin,
1544 const LogicalSize& aSize)
1545 :
1546 #ifdef DEBUG
1547 mWritingMode(aWritingMode),
1548 #endif
1549 mIStart(aOrigin.mPoint.x),
1550 mBStart(aOrigin.mPoint.y),
1551 mISize(aSize.mSize.width),
1552 mBSize(aSize.mSize.height) {
1553 CHECK_WRITING_MODE(aOrigin.GetWritingMode());
1554 CHECK_WRITING_MODE(aSize.GetWritingMode());
1555 }
1556
1557 LogicalRect(WritingMode aWritingMode, const nsRect& aRect,
1558 const nsSize& aContainerSize)
1559 #ifdef DEBUG
1560 : mWritingMode(aWritingMode)
1561 #endif
1562 {
1563 if (aWritingMode.IsVertical()) {
1564 mBStart = aWritingMode.IsVerticalLR()
1565 ? aRect.X()
1566 : aContainerSize.width - aRect.XMost();
1567 mIStart = aWritingMode.IsInlineReversed()
1568 ? aContainerSize.height - aRect.YMost()
1569 : aRect.Y();
1570 mBSize = aRect.Width();
1571 mISize = aRect.Height();
1572 } else {
1573 mIStart = aWritingMode.IsInlineReversed()
1574 ? aContainerSize.width - aRect.XMost()
1575 : aRect.X();
1576 mBStart = aRect.Y();
1577 mISize = aRect.Width();
1578 mBSize = aRect.Height();
1579 }
1580 }
1581
1582 /**
1583 * Inline- and block-dimension geometry.
1584 */
1585 nscoord IStart(WritingMode aWritingMode) const // inline-start edge
1586 {
1587 CHECK_WRITING_MODE(aWritingMode);
1588 return mIStart;
1589 }
1590 nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
1591 {
1592 CHECK_WRITING_MODE(aWritingMode);
1593 return mIStart + mISize;
1594 }
1595 nscoord ISize(WritingMode aWritingMode) const // inline-size
1596 {
1597 CHECK_WRITING_MODE(aWritingMode);
1598 return mISize;
1599 }
1600
1601 nscoord BStart(WritingMode aWritingMode) const // block-start edge
1602 {
1603 CHECK_WRITING_MODE(aWritingMode);
1604 return mBStart;
1605 }
1606 nscoord BEnd(WritingMode aWritingMode) const // block-end edge
1607 {
1608 CHECK_WRITING_MODE(aWritingMode);
1609 return mBStart + mBSize;
1610 }
1611 nscoord BSize(WritingMode aWritingMode) const // block-size
1612 {
1613 CHECK_WRITING_MODE(aWritingMode);
1614 return mBSize;
1615 }
1616
1617 nscoord Start(LogicalAxis aAxis, WritingMode aWM) const {
1618 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1619 }
1620 nscoord End(LogicalAxis aAxis, WritingMode aWM) const {
1621 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1622 }
1623 nscoord Size(LogicalAxis aAxis, WritingMode aWM) const {
1624 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
1625 }
1626
1627 /**
1628 * Writable (reference) accessors are only available for the basic logical
1629 * fields (Start and Size), not derivatives like End.
1630 */
1631 nscoord& IStart(WritingMode aWritingMode) // inline-start edge
1632 {
1633 CHECK_WRITING_MODE(aWritingMode);
1634 return mIStart;
1635 }
1636 nscoord& ISize(WritingMode aWritingMode) // inline-size
1637 {
1638 CHECK_WRITING_MODE(aWritingMode);
1639 return mISize;
1640 }
1641 nscoord& BStart(WritingMode aWritingMode) // block-start edge
1642 {
1643 CHECK_WRITING_MODE(aWritingMode);
1644 return mBStart;
1645 }
1646 nscoord& BSize(WritingMode aWritingMode) // block-size
1647 {
1648 CHECK_WRITING_MODE(aWritingMode);
1649 return mBSize;
1650 }
1651 nscoord& Start(LogicalAxis aAxis, WritingMode aWM) {
1652 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1653 }
1654 nscoord& Size(LogicalAxis aAxis, WritingMode aWM) {
1655 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
1656 }
1657
1658 /**
1659 * Accessors for line-relative coordinates
1660 */
1661 nscoord LineLeft(WritingMode aWritingMode,
1662 const nsSize& aContainerSize) const {
1663 CHECK_WRITING_MODE(aWritingMode);
1664 if (aWritingMode.IsBidiLTR()) {
1665 return IStart();
1666 }
1667 nscoord containerISize = aWritingMode.IsVertical() ? aContainerSize.height
1668 : aContainerSize.width;
1669 return containerISize - IEnd();
1670 }
1671 nscoord LineRight(WritingMode aWritingMode,
1672 const nsSize& aContainerSize) const {
1673 CHECK_WRITING_MODE(aWritingMode);
1674 if (aWritingMode.IsBidiLTR()) {
1675 return IEnd();
1676 }
1677 nscoord containerISize = aWritingMode.IsVertical() ? aContainerSize.height
1678 : aContainerSize.width;
1679 return containerISize - IStart();
1680 }
1681
1682 /**
1683 * Physical coordinates of the rect.
1684 */
1685 nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const {
1686 CHECK_WRITING_MODE(aWritingMode);
1687 if (aWritingMode.IsVertical()) {
1688 return aWritingMode.IsVerticalLR() ? mBStart : aContainerWidth - BEnd();
1689 }
1690 return aWritingMode.IsInlineReversed() ? aContainerWidth - IEnd() : mIStart;
1691 }
1692
1693 nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const {
1694 CHECK_WRITING_MODE(aWritingMode);
1695 if (aWritingMode.IsVertical()) {
1696 return aWritingMode.IsInlineReversed() ? aContainerHeight - IEnd()
1697 : mIStart;
1698 }
1699 return mBStart;
1700 }
1701
1702 nscoord Width(WritingMode aWritingMode) const {
1703 CHECK_WRITING_MODE(aWritingMode);
1704 return aWritingMode.IsVertical() ? mBSize : mISize;
1705 }
1706
1707 nscoord Height(WritingMode aWritingMode) const {
1708 CHECK_WRITING_MODE(aWritingMode);
1709 return aWritingMode.IsVertical() ? mISize : mBSize;
1710 }
1711
1712 nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const {
1713 CHECK_WRITING_MODE(aWritingMode);
1714 if (aWritingMode.IsVertical()) {
1715 return aWritingMode.IsVerticalLR() ? BEnd() : aContainerWidth - mBStart;
1716 }
1717 return aWritingMode.IsInlineReversed() ? aContainerWidth - mIStart : IEnd();
1718 }
1719
1720 nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const {
1721 CHECK_WRITING_MODE(aWritingMode);
1722 if (aWritingMode.IsVertical()) {
1723 return aWritingMode.IsInlineReversed() ? aContainerHeight - mIStart
1724 : IEnd();
1725 }
1726 return BEnd();
1727 }
1728
1729 bool IsEmpty() const { return mISize <= 0 || mBSize <= 0; }
1730
1731 bool IsAllZero() const {
1732 return (mIStart == 0 && mBStart == 0 && mISize == 0 && mBSize == 0);
1733 }
1734
1735 bool IsZeroSize() const { return (mISize == 0 && mBSize == 0); }
1736
1737 void SetEmpty() { mISize = mBSize = 0; }
1738
1739 bool IsEqualEdges(const LogicalRect aOther) const {
1740 CHECK_WRITING_MODE(aOther.GetWritingMode());
1741 bool result = mIStart == aOther.mIStart && mBStart == aOther.mBStart &&
1742 mISize == aOther.mISize && mBSize == aOther.mBSize;
1743
1744 // We want the same result as nsRect, so assert we get it.
1745 MOZ_ASSERT(result ==
1746 nsRect(mIStart, mBStart, mISize, mBSize)
1747 .IsEqualEdges(nsRect(aOther.mIStart, aOther.mBStart,
1748 aOther.mISize, aOther.mBSize)));
1749 return result;
1750 }
1751
1752 LogicalPoint Origin(WritingMode aWritingMode) const {
1753 CHECK_WRITING_MODE(aWritingMode);
1754 return LogicalPoint(aWritingMode, IStart(), BStart());
1755 }
1756 void SetOrigin(WritingMode aWritingMode, const LogicalPoint& aPoint) {
1757 IStart(aWritingMode) = aPoint.I(aWritingMode);
1758 BStart(aWritingMode) = aPoint.B(aWritingMode);
1759 }
1760
1761 LogicalSize Size(WritingMode aWritingMode) const {
1762 CHECK_WRITING_MODE(aWritingMode);
1763 return LogicalSize(aWritingMode, ISize(), BSize());
1764 }
1765
1766 LogicalRect operator+(const LogicalPoint& aPoint) const {
1767 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1768 return LogicalRect(GetWritingMode(), IStart() + aPoint.I(),
1769 BStart() + aPoint.B(), ISize(), BSize());
1770 }
1771
1772 LogicalRect& operator+=(const LogicalPoint& aPoint) {
1773 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1774 mIStart += aPoint.mPoint.x;
1775 mBStart += aPoint.mPoint.y;
1776 return *this;
1777 }
1778
1779 LogicalRect operator-(const LogicalPoint& aPoint) const {
1780 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1781 return LogicalRect(GetWritingMode(), IStart() - aPoint.I(),
1782 BStart() - aPoint.B(), ISize(), BSize());
1783 }
1784
1785 LogicalRect& operator-=(const LogicalPoint& aPoint) {
1786 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1787 mIStart -= aPoint.mPoint.x;
1788 mBStart -= aPoint.mPoint.y;
1789 return *this;
1790 }
1791
1792 void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta) {
1793 CHECK_WRITING_MODE(aWritingMode);
1794 CHECK_WRITING_MODE(aDelta.GetWritingMode());
1795 IStart() += aDelta.I();
1796 BStart() += aDelta.B();
1797 }
1798
1799 void Inflate(nscoord aD) {
1800 #ifdef DEBUG
1801 // Compute using nsRect and assert the results match
1802 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1803 rectDebug.Inflate(aD);
1804 #endif
1805 mIStart -= aD;
1806 mBStart -= aD;
1807 mISize += 2 * aD;
1808 mBSize += 2 * aD;
1809 MOZ_ASSERT(
1810 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1811 }
1812 void Inflate(nscoord aDI, nscoord aDB) {
1813 #ifdef DEBUG
1814 // Compute using nsRect and assert the results match
1815 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1816 rectDebug.Inflate(aDI, aDB);
1817 #endif
1818 mIStart -= aDI;
1819 mBStart -= aDB;
1820 mISize += 2 * aDI;
1821 mBSize += 2 * aDB;
1822 MOZ_ASSERT(
1823 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1824 }
1825 void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin) {
1826 CHECK_WRITING_MODE(aWritingMode);
1827 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1828 #ifdef DEBUG
1829 // Compute using nsRect and assert the results match
1830 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1831 rectDebug.Inflate(aMargin.mMargin);
1832 #endif
1833 mIStart -= aMargin.mMargin.left;
1834 mBStart -= aMargin.mMargin.top;
1835 mISize += aMargin.mMargin.LeftRight();
1836 mBSize += aMargin.mMargin.TopBottom();
1837 MOZ_ASSERT(
1838 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1839 }
1840
1841 void Deflate(nscoord aD) {
1842 #ifdef DEBUG
1843 // Compute using nsRect and assert the results match
1844 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1845 rectDebug.Deflate(aD);
1846 #endif
1847 mIStart += aD;
1848 mBStart += aD;
1849 mISize = std::max(0, mISize - 2 * aD);
1850 mBSize = std::max(0, mBSize - 2 * aD);
1851 MOZ_ASSERT(
1852 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1853 }
1854 void Deflate(nscoord aDI, nscoord aDB) {
1855 #ifdef DEBUG
1856 // Compute using nsRect and assert the results match
1857 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1858 rectDebug.Deflate(aDI, aDB);
1859 #endif
1860 mIStart += aDI;
1861 mBStart += aDB;
1862 mISize = std::max(0, mISize - 2 * aDI);
1863 mBSize = std::max(0, mBSize - 2 * aDB);
1864 MOZ_ASSERT(
1865 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1866 }
1867 void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin) {
1868 CHECK_WRITING_MODE(aWritingMode);
1869 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1870 #ifdef DEBUG
1871 // Compute using nsRect and assert the results match
1872 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1873 rectDebug.Deflate(aMargin.mMargin);
1874 #endif
1875 mIStart += aMargin.mMargin.left;
1876 mBStart += aMargin.mMargin.top;
1877 mISize = std::max(0, mISize - aMargin.mMargin.LeftRight());
1878 mBSize = std::max(0, mBSize - aMargin.mMargin.TopBottom());
1879 MOZ_ASSERT(
1880 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1881 }
1882
1883 /**
1884 * Return an nsRect containing our physical coordinates within the given
1885 * container size.
1886 */
1887 nsRect GetPhysicalRect(WritingMode aWritingMode,
1888 const nsSize& aContainerSize) const {
1889 CHECK_WRITING_MODE(aWritingMode);
1890 if (aWritingMode.IsVertical()) {
1891 return nsRect(aWritingMode.IsVerticalLR() ? BStart()
1892 : aContainerSize.width - BEnd(),
1893 aWritingMode.IsInlineReversed()
1894 ? aContainerSize.height - IEnd()
1895 : IStart(),
1896 BSize(), ISize());
1897 } else {
1898 return nsRect(aWritingMode.IsInlineReversed()
1899 ? aContainerSize.width - IEnd()
1900 : IStart(),
1901 BStart(), ISize(), BSize());
1902 }
1903 }
1904
1905 /**
1906 * Return a LogicalRect representing this rect in a different writing mode
1907 */
1908 LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode,
1909 const nsSize& aContainerSize) const {
1910 CHECK_WRITING_MODE(aFromMode);
1911 return aToMode == aFromMode
1912 ? *this
1913 : LogicalRect(aToMode,
1914 GetPhysicalRect(aFromMode, aContainerSize),
1915 aContainerSize);
1916 }
1917
1918 /**
1919 * Set *this to be the rectangle containing the intersection of aRect1
1920 * and aRect2, return whether the intersection is non-empty.
1921 */
1922 bool IntersectRect(const LogicalRect& aRect1, const LogicalRect& aRect2) {
1923 CHECK_WRITING_MODE(aRect1.mWritingMode);
1924 CHECK_WRITING_MODE(aRect2.mWritingMode);
1925 #ifdef DEBUG
1926 // Compute using nsRect and assert the results match
1927 nsRect rectDebug;
1928 rectDebug.IntersectRect(
1929 nsRect(aRect1.mIStart, aRect1.mBStart, aRect1.mISize, aRect1.mBSize),
1930 nsRect(aRect2.mIStart, aRect2.mBStart, aRect2.mISize, aRect2.mBSize));
1931 #endif
1932
1933 nscoord iEnd = std::min(aRect1.IEnd(), aRect2.IEnd());
1934 mIStart = std::max(aRect1.mIStart, aRect2.mIStart);
1935 mISize = iEnd - mIStart;
1936
1937 nscoord bEnd = std::min(aRect1.BEnd(), aRect2.BEnd());
1938 mBStart = std::max(aRect1.mBStart, aRect2.mBStart);
1939 mBSize = bEnd - mBStart;
1940
1941 if (mISize < 0 || mBSize < 0) {
1942 mISize = 0;
1943 mBSize = 0;
1944 }
1945
1946 MOZ_ASSERT(
1947 (rectDebug.IsEmpty() && (mISize == 0 || mBSize == 0)) ||
1948 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1949 return mISize > 0 && mBSize > 0;
1950 }
1951
1952 friend std::ostream& operator<<(std::ostream& aStream,
1953 const LogicalRect& aRect) {
1954 return aStream << '(' << aRect.IStart() << ',' << aRect.BStart() << ','
1955 << aRect.ISize() << ',' << aRect.BSize() << ')';
1956 }
1957
1958 private:
1959 LogicalRect() = delete;
1960
1961 #ifdef DEBUG
1962 WritingMode GetWritingMode() const { return mWritingMode; }
1963 #else
1964 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1965 #endif
1966
1967 nscoord IStart() const // inline-start edge
1968 {
1969 return mIStart;
1970 }
1971 nscoord IEnd() const // inline-end edge
1972 {
1973 return mIStart + mISize;
1974 }
1975 nscoord ISize() const // inline-size
1976 {
1977 return mISize;
1978 }
1979
1980 nscoord BStart() const // block-start edge
1981 {
1982 return mBStart;
1983 }
1984 nscoord BEnd() const // block-end edge
1985 {
1986 return mBStart + mBSize;
1987 }
1988 nscoord BSize() const // block-size
1989 {
1990 return mBSize;
1991 }
1992
1993 nscoord& IStart() // inline-start edge
1994 {
1995 return mIStart;
1996 }
1997 nscoord& ISize() // inline-size
1998 {
1999 return mISize;
2000 }
2001 nscoord& BStart() // block-start edge
2002 {
2003 return mBStart;
2004 }
2005 nscoord& BSize() // block-size
2006 {
2007 return mBSize;
2008 }
2009
2010 #ifdef DEBUG
2011 WritingMode mWritingMode;
2012 #endif
2013 // Inline- and block-geometry dimension
2014 nscoord mIStart; // inline-start edge
2015 nscoord mBStart; // block-start edge
2016 nscoord mISize; // inline-size
2017 nscoord mBSize; // block-size
2018 };
2019
2020 template <typename T>
2021 const T& StyleRect<T>::Get(WritingMode aWM, LogicalSide aSide) const {
2022 return Get(aWM.PhysicalSide(aSide));
2023 }
2024
2025 template <typename T>
2026 const T& StyleRect<T>::GetIStart(WritingMode aWM) const {
2027 return Get(aWM, eLogicalSideIStart);
2028 }
2029
2030 template <typename T>
2031 const T& StyleRect<T>::GetBStart(WritingMode aWM) const {
2032 return Get(aWM, eLogicalSideBStart);
2033 }
2034
2035 template <typename T>
2036 const T& StyleRect<T>::GetIEnd(WritingMode aWM) const {
2037 return Get(aWM, eLogicalSideIEnd);
2038 }
2039
2040 template <typename T>
2041 const T& StyleRect<T>::GetBEnd(WritingMode aWM) const {
2042 return Get(aWM, eLogicalSideBEnd);
2043 }
2044
2045 template <typename T>
2046 T& StyleRect<T>::Get(WritingMode aWM, LogicalSide aSide) {
2047 return Get(aWM.PhysicalSide(aSide));
2048 }
2049
2050 template <typename T>
2051 T& StyleRect<T>::GetIStart(WritingMode aWM) {
2052 return Get(aWM, eLogicalSideIStart);
2053 }
2054
2055 template <typename T>
2056 T& StyleRect<T>::GetBStart(WritingMode aWM) {
2057 return Get(aWM, eLogicalSideBStart);
2058 }
2059
2060 template <typename T>
2061 T& StyleRect<T>::GetIEnd(WritingMode aWM) {
2062 return Get(aWM, eLogicalSideIEnd);
2063 }
2064
2065 template <typename T>
2066 T& StyleRect<T>::GetBEnd(WritingMode aWM) {
2067 return Get(aWM, eLogicalSideBEnd);
2068 }
2069
2070 template <typename T>
2071 const T& StyleRect<T>::Start(mozilla::LogicalAxis aAxis,
2072 mozilla::WritingMode aWM) const {
2073 return Get(aWM, aAxis == mozilla::eLogicalAxisInline
2074 ? mozilla::eLogicalSideIStart
2075 : mozilla::eLogicalSideBStart);
2076 }
2077
2078 template <typename T>
2079 const T& StyleRect<T>::End(mozilla::LogicalAxis aAxis,
2080 mozilla::WritingMode aWM) const {
2081 return Get(aWM, aAxis == mozilla::eLogicalAxisInline
2082 ? mozilla::eLogicalSideIEnd
2083 : mozilla::eLogicalSideBEnd);
2084 }
2085
2086 } // namespace mozilla
2087
2088 // Definitions of inline methods for nsStylePosition, declared in
2089 // nsStyleStruct.h but not defined there because they need WritingMode.
2090 inline const mozilla::StyleSize& nsStylePosition::ISize(WritingMode aWM) const {
2091 return aWM.IsVertical() ? mHeight : mWidth;
2092 }
2093 inline const mozilla::StyleSize& nsStylePosition::MinISize(
2094 WritingMode aWM) const {
2095 return aWM.IsVertical() ? mMinHeight : mMinWidth;
2096 }
2097 inline const mozilla::StyleMaxSize& nsStylePosition::MaxISize(
2098 WritingMode aWM) const {
2099 return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
2100 }
2101 inline const mozilla::StyleSize& nsStylePosition::BSize(WritingMode aWM) const {
2102 return aWM.IsVertical() ? mWidth : mHeight;
2103 }
2104 inline const mozilla::StyleSize& nsStylePosition::MinBSize(
2105 WritingMode aWM) const {
2106 return aWM.IsVertical() ? mMinWidth : mMinHeight;
2107 }
2108 inline const mozilla::StyleMaxSize& nsStylePosition::MaxBSize(
2109 WritingMode aWM) const {
2110 return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
2111 }
2112 inline const mozilla::StyleSize& nsStylePosition::Size(
2113 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2114 return aAxis == mozilla::eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
2115 }
2116 inline const mozilla::StyleSize& nsStylePosition::MinSize(
2117 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2118 return aAxis == mozilla::eLogicalAxisInline ? MinISize(aWM) : MinBSize(aWM);
2119 }
2120 inline const mozilla::StyleMaxSize& nsStylePosition::MaxSize(
2121 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2122 return aAxis == mozilla::eLogicalAxisInline ? MaxISize(aWM) : MaxBSize(aWM);
2123 }
2124
2125 inline bool nsStylePosition::ISizeDependsOnContainer(WritingMode aWM) const {
2126 const auto& iSize = ISize(aWM);
2127 return iSize.IsAuto() || ISizeCoordDependsOnContainer(iSize);
2128 }
2129 inline bool nsStylePosition::MinISizeDependsOnContainer(WritingMode aWM) const {
2130 // NOTE: For a flex item, "min-inline-size:auto" is supposed to behave like
2131 // "min-content", which does depend on the container, so you might think we'd
2132 // need a special case for "flex item && min-inline-size:auto" here. However,
2133 // we don't actually need that special-case code, because flex items are
2134 // explicitly supposed to *ignore* their min-inline-size (i.e. behave like
2135 // it's 0) until the flex container explicitly considers it. So -- since the
2136 // flex container doesn't rely on this method, we don't need to worry about
2137 // special behavior for flex items' "min-inline-size:auto" values here.
2138 return ISizeCoordDependsOnContainer(MinISize(aWM));
2139 }
2140 inline bool nsStylePosition::MaxISizeDependsOnContainer(WritingMode aWM) const {
2141 // NOTE: The comment above MinISizeDependsOnContainer about flex items
2142 // applies here, too.
2143 return ISizeCoordDependsOnContainer(MaxISize(aWM));
2144 }
2145 // Note that these functions count `auto` as depending on the container
2146 // since that's the case for absolutely positioned elements.
2147 // However, some callers do not care about this case and should check
2148 // for it, since it is the most common case.
2149 // FIXME: We should probably change the assumption to be the other way
2150 // around.
2151 inline bool nsStylePosition::BSizeDependsOnContainer(WritingMode aWM) const {
2152 const auto& bSize = BSize(aWM);
2153 return bSize.BehavesLikeInitialValueOnBlockAxis() ||
2154 BSizeCoordDependsOnContainer(bSize);
2155 }
2156 inline bool nsStylePosition::MinBSizeDependsOnContainer(WritingMode aWM) const {
2157 return BSizeCoordDependsOnContainer(MinBSize(aWM));
2158 }
2159 inline bool nsStylePosition::MaxBSizeDependsOnContainer(WritingMode aWM) const {
2160 return BSizeCoordDependsOnContainer(MaxBSize(aWM));
2161 }
2162
2163 inline bool nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const {
2164 return mMargin.GetBStart(aWM).IsAuto() || mMargin.GetBEnd(aWM).IsAuto();
2165 }
2166
2167 inline bool nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const {
2168 return mMargin.GetIStart(aWM).IsAuto() || mMargin.GetIEnd(aWM).IsAuto();
2169 }
2170 inline bool nsStyleMargin::HasAuto(mozilla::LogicalAxis aAxis,
2171 mozilla::WritingMode aWM) const {
2172 return aAxis == mozilla::eLogicalAxisInline ? HasInlineAxisAuto(aWM)
2173 : HasBlockAxisAuto(aWM);
2174 }
2175
2176 inline mozilla::StyleAlignFlags nsStylePosition::UsedSelfAlignment(
2177 mozilla::LogicalAxis aAxis, const mozilla::ComputedStyle* aParent) const {
2178 return aAxis == mozilla::eLogicalAxisBlock ? UsedAlignSelf(aParent)._0
2179 : UsedJustifySelf(aParent)._0;
2180 }
2181
2182 inline mozilla::StyleContentDistribution nsStylePosition::UsedContentAlignment(
2183 mozilla::LogicalAxis aAxis) const {
2184 return aAxis == mozilla::eLogicalAxisBlock ? mAlignContent : mJustifyContent;
2185 }
2186
2187 inline mozilla::StyleContentDistribution nsStylePosition::UsedTracksAlignment(
2188 mozilla::LogicalAxis aAxis, uint32_t aIndex) const {
2189 using T = mozilla::StyleAlignFlags;
2190 const auto& tracksAlignment =
2191 aAxis == mozilla::eLogicalAxisBlock ? mAlignTracks : mJustifyTracks;
2192 if (MOZ_LIKELY(tracksAlignment.IsEmpty())) {
2193 // An empty array encodes the initial value, 'normal', which behaves as
2194 // 'start' for Grid containers.
2195 return mozilla::StyleContentDistribution{T::START};
2196 }
2197
2198 // If there are fewer values than tracks, then the last value is used for all
2199 // the remaining tracks.
2200 const auto& ta = tracksAlignment.AsSpan();
2201 auto align = ta[std::min<size_t>(aIndex, ta.Length() - 1)];
2202 if (align.primary == T::NORMAL) {
2203 align = mozilla::StyleContentDistribution{T::START};
2204 }
2205 return align;
2206 }
2207
2208 #endif // WritingModes_h_
2209