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/intl/BidiEmbeddingLevel.h"
13 #include "mozilla/ComputedStyle.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(uint8_t(StyleWritingModeProperty::HorizontalTb) == 0 &&
321 uint8_t(StyleWritingModeProperty::VerticalRl) == 1 &&
322 uint8_t(StyleWritingModeProperty::VerticalLr) == 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 StyleWritingModel::HorizontalTB or
332 // StyleWritingModeProperty::VerticalRL, 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 StyleWritingModeProperty 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 SidewaysMask bit of the writing-mode value, as this has no
354 // effect on the side mappings.
355 aWritingModeValue &= ~kWritingModeSidewaysMask;
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(mozilla::intl::BidiEmbeddingLevel level)528 void SetDirectionFromBidiLevel(mozilla::intl::BidiEmbeddingLevel level) {
529 if (level.IsRTL() == 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 << (aWM.IsVertical()
618 ? aWM.IsVerticalLR() ? aWM.IsBidiLTR()
619 ? aWM.IsSideways()
620 ? "sw-lr-ltr"
621 : "v-lr-ltr"
622 : aWM.IsSideways() ? "sw-lr-rtl"
623 : "v-lr-rtl"
624 : aWM.IsBidiLTR()
625 ? aWM.IsSideways() ? "sw-rl-ltr" : "v-rl-ltr"
626 : aWM.IsSideways() ? "sw-rl-rtl"
627 : "v-rl-rtl"
628 : aWM.IsBidiLTR() ? "h-ltr"
629 : "h-rtl");
630 }
631
632 /**
633 * Logical-coordinate classes:
634 *
635 * There are three sets of coordinate space:
636 * - physical (top, left, bottom, right)
637 * relative to graphics coord system
638 * - flow-relative (block-start, inline-start, block-end, inline-end)
639 * relative to block/inline flow directions
640 * - line-relative (line-over, line-left, line-under, line-right)
641 * relative to glyph orientation / inline bidi directions
642 * See CSS3 Writing Modes for more information
643 * http://www.w3.org/TR/css3-writing-modes/#abstract-box
644 *
645 * For shorthand, B represents the block-axis
646 * I represents the inline-axis
647 *
648 * The flow-relative geometric classes store coords in flow-relative space.
649 * They use a private ns{Point,Size,Rect,Margin} member to store the actual
650 * coordinate values, but reinterpret them as logical instead of physical.
651 * This allows us to easily perform calculations in logical space (provided
652 * writing modes of the operands match), by simply mapping to nsPoint (etc)
653 * methods.
654 *
655 * Physical-coordinate accessors/setters are responsible to translate these
656 * internal logical values as necessary.
657 *
658 * In DEBUG builds, the logical types store their WritingMode and check
659 * that the same WritingMode is passed whenever callers ask them to do a
660 * writing-mode-dependent operation. Non-DEBUG builds do NOT check this,
661 * to avoid the overhead of storing WritingMode fields.
662 *
663 * Open question: do we need a different set optimized for line-relative
664 * math, for use in nsLineLayout and the like? Or is multiplying values
665 * by FlowRelativeToLineRelativeFactor() enough?
666 */
667
668 /**
669 * Flow-relative point
670 */
671 class LogicalPoint {
672 public:
673 explicit LogicalPoint(WritingMode aWritingMode)
674 :
675 #ifdef DEBUG
676 mWritingMode(aWritingMode),
677 #endif
678 mPoint(0, 0) {
679 }
680
681 // Construct from a writing mode and individual coordinates (which MUST be
682 // values in that writing mode, NOT physical coordinates!)
683 LogicalPoint(WritingMode aWritingMode, nscoord aI, nscoord aB)
684 :
685 #ifdef DEBUG
686 mWritingMode(aWritingMode),
687 #endif
688 mPoint(aI, aB) {
689 }
690
691 // Construct from a writing mode and a physical point, within a given
692 // containing rectangle's size (defining the conversion between LTR
693 // and RTL coordinates, and between TTB and BTT coordinates).
694 LogicalPoint(WritingMode aWritingMode, const nsPoint& aPoint,
695 const nsSize& aContainerSize)
696 #ifdef DEBUG
697 : mWritingMode(aWritingMode)
698 #endif
699 {
700 if (aWritingMode.IsVertical()) {
701 I() = aWritingMode.IsInlineReversed() ? aContainerSize.height - aPoint.y
702 : aPoint.y;
703 B() = aWritingMode.IsVerticalLR() ? aPoint.x
704 : aContainerSize.width - aPoint.x;
705 } else {
706 I() = aWritingMode.IsInlineReversed() ? aContainerSize.width - aPoint.x
707 : aPoint.x;
708 B() = aPoint.y;
709 }
710 }
711
712 /**
713 * Read-only (const) access to the logical coordinates.
714 */
715 nscoord I(WritingMode aWritingMode) const // inline-axis
716 {
717 CHECK_WRITING_MODE(aWritingMode);
718 return mPoint.x;
719 }
720 nscoord B(WritingMode aWritingMode) const // block-axis
721 {
722 CHECK_WRITING_MODE(aWritingMode);
723 return mPoint.y;
724 }
725 nscoord Pos(LogicalAxis aAxis, WritingMode aWM) const {
726 return aAxis == eLogicalAxisInline ? I(aWM) : B(aWM);
727 }
728 nscoord LineRelative(WritingMode aWritingMode,
729 const nsSize& aContainerSize) const // line-axis
730 {
731 CHECK_WRITING_MODE(aWritingMode);
732 if (aWritingMode.IsBidiLTR()) {
733 return I();
734 }
735 return (aWritingMode.IsVertical() ? aContainerSize.height
736 : aContainerSize.width) -
737 I();
738 }
739
740 /**
741 * These non-const accessors return a reference (lvalue) that can be
742 * assigned to by callers.
743 */
744 nscoord& I(WritingMode aWritingMode) // inline-axis
745 {
746 CHECK_WRITING_MODE(aWritingMode);
747 return mPoint.x;
748 }
749 nscoord& B(WritingMode aWritingMode) // block-axis
750 {
751 CHECK_WRITING_MODE(aWritingMode);
752 return mPoint.y;
753 }
754 nscoord& Pos(LogicalAxis aAxis, WritingMode aWM) {
755 return aAxis == eLogicalAxisInline ? I(aWM) : B(aWM);
756 }
757
758 /**
759 * Return a physical point corresponding to our logical coordinates,
760 * converted according to our writing mode.
761 */
762 nsPoint GetPhysicalPoint(WritingMode aWritingMode,
763 const nsSize& aContainerSize) const {
764 CHECK_WRITING_MODE(aWritingMode);
765 if (aWritingMode.IsVertical()) {
766 return nsPoint(
767 aWritingMode.IsVerticalLR() ? B() : aContainerSize.width - B(),
768 aWritingMode.IsInlineReversed() ? aContainerSize.height - I() : I());
769 } else {
770 return nsPoint(
771 aWritingMode.IsInlineReversed() ? aContainerSize.width - I() : I(),
772 B());
773 }
774 }
775
776 /**
777 * Return the equivalent point in a different writing mode.
778 */
779 LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
780 const nsSize& aContainerSize) const {
781 CHECK_WRITING_MODE(aFromMode);
782 return aToMode == aFromMode
783 ? *this
784 : LogicalPoint(aToMode,
785 GetPhysicalPoint(aFromMode, aContainerSize),
786 aContainerSize);
787 }
788
789 bool operator==(const LogicalPoint& aOther) const {
790 CHECK_WRITING_MODE(aOther.GetWritingMode());
791 return mPoint == aOther.mPoint;
792 }
793
794 bool operator!=(const LogicalPoint& aOther) const {
795 CHECK_WRITING_MODE(aOther.GetWritingMode());
796 return mPoint != aOther.mPoint;
797 }
798
799 LogicalPoint operator+(const LogicalPoint& aOther) const {
800 CHECK_WRITING_MODE(aOther.GetWritingMode());
801 // In non-debug builds, LogicalPoint does not store the WritingMode,
802 // so the first parameter here (which will always be eUnknownWritingMode)
803 // is ignored.
804 return LogicalPoint(GetWritingMode(), mPoint.x + aOther.mPoint.x,
805 mPoint.y + aOther.mPoint.y);
806 }
807
808 LogicalPoint& operator+=(const LogicalPoint& aOther) {
809 CHECK_WRITING_MODE(aOther.GetWritingMode());
810 I() += aOther.I();
811 B() += aOther.B();
812 return *this;
813 }
814
815 LogicalPoint operator-(const LogicalPoint& aOther) const {
816 CHECK_WRITING_MODE(aOther.GetWritingMode());
817 // In non-debug builds, LogicalPoint does not store the WritingMode,
818 // so the first parameter here (which will always be eUnknownWritingMode)
819 // is ignored.
820 return LogicalPoint(GetWritingMode(), mPoint.x - aOther.mPoint.x,
821 mPoint.y - aOther.mPoint.y);
822 }
823
824 LogicalPoint& operator-=(const LogicalPoint& aOther) {
825 CHECK_WRITING_MODE(aOther.GetWritingMode());
826 I() -= aOther.I();
827 B() -= aOther.B();
828 return *this;
829 }
830
831 friend std::ostream& operator<<(std::ostream& aStream,
832 const LogicalPoint& aPoint) {
833 return aStream << aPoint.mPoint;
834 }
835
836 private:
837 friend class LogicalRect;
838
839 /**
840 * NOTE that in non-DEBUG builds, GetWritingMode() always returns
841 * eUnknownWritingMode, as the current mode is not stored in the logical-
842 * geometry classes. Therefore, this method is private; it is used ONLY
843 * by the DEBUG-mode checking macros in this class and its friends;
844 * other code is not allowed to ask a logical point for its writing mode,
845 * as this info will simply not be available in non-DEBUG builds.
846 *
847 * Also, in non-DEBUG builds, CHECK_WRITING_MODE does nothing, and the
848 * WritingMode parameter to logical methods will generally be optimized
849 * away altogether.
850 */
851 #ifdef DEBUG
852 WritingMode GetWritingMode() const { return mWritingMode; }
853 #else
854 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
855 #endif
856
857 // We don't allow construction of a LogicalPoint with no writing mode.
858 LogicalPoint() = delete;
859
860 // Accessors that don't take or check a WritingMode value.
861 // These are for internal use only; they are called by methods that have
862 // themselves already checked the WritingMode passed by the caller.
863 nscoord I() const // inline-axis
864 {
865 return mPoint.x;
866 }
867 nscoord B() const // block-axis
868 {
869 return mPoint.y;
870 }
871
872 nscoord& I() // inline-axis
873 {
874 return mPoint.x;
875 }
876 nscoord& B() // block-axis
877 {
878 return mPoint.y;
879 }
880
881 #ifdef DEBUG
882 WritingMode mWritingMode;
883 #endif
884
885 // We use an nsPoint to hold the coordinates, but reinterpret its .x and .y
886 // fields as the inline and block directions. Hence, this is not exposed
887 // directly, but only through accessors that will map them according to the
888 // writing mode.
889 nsPoint mPoint;
890 };
891
892 /**
893 * Flow-relative size
894 */
895 class LogicalSize {
896 public:
897 explicit LogicalSize(WritingMode aWritingMode)
898 :
899 #ifdef DEBUG
900 mWritingMode(aWritingMode),
901 #endif
902 mSize(0, 0) {
903 }
904
905 LogicalSize(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
906 :
907 #ifdef DEBUG
908 mWritingMode(aWritingMode),
909 #endif
910 mSize(aISize, aBSize) {
911 }
912
913 LogicalSize(WritingMode aWritingMode, const nsSize& aPhysicalSize)
914 #ifdef DEBUG
915 : mWritingMode(aWritingMode)
916 #endif
917 {
918 if (aWritingMode.IsVertical()) {
919 ISize() = aPhysicalSize.height;
920 BSize() = aPhysicalSize.width;
921 } else {
922 ISize() = aPhysicalSize.width;
923 BSize() = aPhysicalSize.height;
924 }
925 }
926
927 void SizeTo(WritingMode aWritingMode, nscoord aISize, nscoord aBSize) {
928 CHECK_WRITING_MODE(aWritingMode);
929 mSize.SizeTo(aISize, aBSize);
930 }
931
932 /**
933 * Dimensions in logical and physical terms
934 */
935 nscoord ISize(WritingMode aWritingMode) const // inline-size
936 {
937 CHECK_WRITING_MODE(aWritingMode);
938 return mSize.width;
939 }
940 nscoord BSize(WritingMode aWritingMode) const // block-size
941 {
942 CHECK_WRITING_MODE(aWritingMode);
943 return mSize.height;
944 }
945 nscoord Size(LogicalAxis aAxis, WritingMode aWM) const {
946 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
947 }
948
949 nscoord Width(WritingMode aWritingMode) const {
950 CHECK_WRITING_MODE(aWritingMode);
951 return aWritingMode.IsVertical() ? BSize() : ISize();
952 }
953 nscoord Height(WritingMode aWritingMode) const {
954 CHECK_WRITING_MODE(aWritingMode);
955 return aWritingMode.IsVertical() ? ISize() : BSize();
956 }
957
958 /**
959 * Writable references to the logical dimensions
960 */
961 nscoord& ISize(WritingMode aWritingMode) // inline-size
962 {
963 CHECK_WRITING_MODE(aWritingMode);
964 return mSize.width;
965 }
966 nscoord& BSize(WritingMode aWritingMode) // block-size
967 {
968 CHECK_WRITING_MODE(aWritingMode);
969 return mSize.height;
970 }
971 nscoord& Size(LogicalAxis aAxis, WritingMode aWM) {
972 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
973 }
974
975 /**
976 * Return an nsSize containing our physical dimensions
977 */
978 nsSize GetPhysicalSize(WritingMode aWritingMode) const {
979 CHECK_WRITING_MODE(aWritingMode);
980 return aWritingMode.IsVertical() ? nsSize(BSize(), ISize())
981 : nsSize(ISize(), BSize());
982 }
983
984 /**
985 * Return a LogicalSize representing this size in a different writing mode
986 */
987 LogicalSize ConvertTo(WritingMode aToMode, WritingMode aFromMode) const {
988 #ifdef DEBUG
989 // In DEBUG builds make sure to return a LogicalSize with the
990 // expected writing mode
991 CHECK_WRITING_MODE(aFromMode);
992 return aToMode == aFromMode
993 ? *this
994 : LogicalSize(aToMode, GetPhysicalSize(aFromMode));
995 #else
996 // optimization for non-DEBUG builds where LogicalSize doesn't store
997 // the writing mode
998 return (aToMode == aFromMode || !aToMode.IsOrthogonalTo(aFromMode))
999 ? *this
1000 : LogicalSize(aToMode, BSize(), ISize());
1001 #endif
1002 }
1003
1004 /**
1005 * Test if a size is (0, 0).
1006 */
1007 bool IsAllZero() const { return ISize() == 0 && BSize() == 0; }
1008
1009 /**
1010 * Various binary operators on LogicalSize. These are valid ONLY for operands
1011 * that share the same writing mode.
1012 */
1013 bool operator==(const LogicalSize& aOther) const {
1014 CHECK_WRITING_MODE(aOther.GetWritingMode());
1015 return mSize == aOther.mSize;
1016 }
1017
1018 bool operator!=(const LogicalSize& aOther) const {
1019 CHECK_WRITING_MODE(aOther.GetWritingMode());
1020 return mSize != aOther.mSize;
1021 }
1022
1023 LogicalSize operator+(const LogicalSize& aOther) const {
1024 CHECK_WRITING_MODE(aOther.GetWritingMode());
1025 return LogicalSize(GetWritingMode(), ISize() + aOther.ISize(),
1026 BSize() + aOther.BSize());
1027 }
1028 LogicalSize& operator+=(const LogicalSize& aOther) {
1029 CHECK_WRITING_MODE(aOther.GetWritingMode());
1030 ISize() += aOther.ISize();
1031 BSize() += aOther.BSize();
1032 return *this;
1033 }
1034
1035 LogicalSize operator-(const LogicalSize& aOther) const {
1036 CHECK_WRITING_MODE(aOther.GetWritingMode());
1037 return LogicalSize(GetWritingMode(), ISize() - aOther.ISize(),
1038 BSize() - aOther.BSize());
1039 }
1040 LogicalSize& operator-=(const LogicalSize& aOther) {
1041 CHECK_WRITING_MODE(aOther.GetWritingMode());
1042 ISize() -= aOther.ISize();
1043 BSize() -= aOther.BSize();
1044 return *this;
1045 }
1046
1047 friend std::ostream& operator<<(std::ostream& aStream,
1048 const LogicalSize& aSize) {
1049 return aStream << aSize.mSize;
1050 }
1051
1052 private:
1053 friend class LogicalRect;
1054
1055 LogicalSize() = delete;
1056
1057 #ifdef DEBUG
1058 WritingMode GetWritingMode() const { return mWritingMode; }
1059 #else
1060 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1061 #endif
1062
1063 nscoord ISize() const // inline-size
1064 {
1065 return mSize.width;
1066 }
1067 nscoord BSize() const // block-size
1068 {
1069 return mSize.height;
1070 }
1071
1072 nscoord& ISize() // inline-size
1073 {
1074 return mSize.width;
1075 }
1076 nscoord& BSize() // block-size
1077 {
1078 return mSize.height;
1079 }
1080
1081 #ifdef DEBUG
1082 WritingMode mWritingMode;
1083 #endif
1084 nsSize mSize;
1085 };
1086
1087 /**
1088 * LogicalSides represents a set of logical sides.
1089 */
1090 struct LogicalSides final {
1091 explicit LogicalSides(WritingMode aWritingMode)
1092 :
1093 #ifdef DEBUG
1094 mWritingMode(aWritingMode),
1095 #endif
1096 mBits(0) {
1097 }
1098 LogicalSides(WritingMode aWritingMode, LogicalSideBits aSideBits)
1099 :
1100 #ifdef DEBUG
1101 mWritingMode(aWritingMode),
1102 #endif
1103 mBits(aSideBits) {
1104 MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
1105 }
1106 bool IsEmpty() const { return mBits == 0; }
1107 bool BStart() const { return mBits & eLogicalSideBitsBStart; }
1108 bool BEnd() const { return mBits & eLogicalSideBitsBEnd; }
1109 bool IStart() const { return mBits & eLogicalSideBitsIStart; }
1110 bool IEnd() const { return mBits & eLogicalSideBitsIEnd; }
1111 bool Contains(LogicalSideBits aSideBits) const {
1112 MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
1113 return (mBits & aSideBits) == aSideBits;
1114 }
1115 LogicalSides operator|(LogicalSides aOther) const {
1116 CHECK_WRITING_MODE(aOther.GetWritingMode());
1117 return *this | LogicalSideBits(aOther.mBits);
1118 }
1119 LogicalSides operator|(LogicalSideBits aSideBits) const {
1120 return LogicalSides(GetWritingMode(), LogicalSideBits(mBits | aSideBits));
1121 }
1122 LogicalSides& operator|=(LogicalSides aOther) {
1123 CHECK_WRITING_MODE(aOther.GetWritingMode());
1124 return *this |= LogicalSideBits(aOther.mBits);
1125 }
1126 LogicalSides& operator|=(LogicalSideBits aSideBits) {
1127 mBits |= aSideBits;
1128 return *this;
1129 }
1130 bool operator==(LogicalSides aOther) const {
1131 CHECK_WRITING_MODE(aOther.GetWritingMode());
1132 return mBits == aOther.mBits;
1133 }
1134 bool operator!=(LogicalSides aOther) const {
1135 CHECK_WRITING_MODE(aOther.GetWritingMode());
1136 return !(*this == aOther);
1137 }
1138
1139 #ifdef DEBUG
1140 WritingMode GetWritingMode() const { return mWritingMode; }
1141 #else
1142 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1143 #endif
1144
1145 private:
1146 #ifdef DEBUG
1147 WritingMode mWritingMode;
1148 #endif
1149 uint8_t mBits;
1150 };
1151
1152 /**
1153 * Flow-relative margin
1154 */
1155 class LogicalMargin {
1156 public:
1157 explicit LogicalMargin(WritingMode aWritingMode)
1158 :
1159 #ifdef DEBUG
1160 mWritingMode(aWritingMode),
1161 #endif
1162 mMargin(0, 0, 0, 0) {
1163 }
1164
1165 LogicalMargin(WritingMode aWritingMode, nscoord aBStart, nscoord aIEnd,
1166 nscoord aBEnd, nscoord aIStart)
1167 :
1168 #ifdef DEBUG
1169 mWritingMode(aWritingMode),
1170 #endif
1171 mMargin(aBStart, aIEnd, aBEnd, aIStart) {
1172 }
1173
1174 LogicalMargin(WritingMode aWritingMode, const nsMargin& aPhysicalMargin)
1175 #ifdef DEBUG
1176 : mWritingMode(aWritingMode)
1177 #endif
1178 {
1179 if (aWritingMode.IsVertical()) {
1180 if (aWritingMode.IsVerticalLR()) {
1181 mMargin.top = aPhysicalMargin.left;
1182 mMargin.bottom = aPhysicalMargin.right;
1183 } else {
1184 mMargin.top = aPhysicalMargin.right;
1185 mMargin.bottom = aPhysicalMargin.left;
1186 }
1187 if (aWritingMode.IsInlineReversed()) {
1188 mMargin.left = aPhysicalMargin.bottom;
1189 mMargin.right = aPhysicalMargin.top;
1190 } else {
1191 mMargin.left = aPhysicalMargin.top;
1192 mMargin.right = aPhysicalMargin.bottom;
1193 }
1194 } else {
1195 mMargin.top = aPhysicalMargin.top;
1196 mMargin.bottom = aPhysicalMargin.bottom;
1197 if (aWritingMode.IsInlineReversed()) {
1198 mMargin.left = aPhysicalMargin.right;
1199 mMargin.right = aPhysicalMargin.left;
1200 } else {
1201 mMargin.left = aPhysicalMargin.left;
1202 mMargin.right = aPhysicalMargin.right;
1203 }
1204 }
1205 }
1206
1207 nscoord IStart(WritingMode aWritingMode) const // inline-start margin
1208 {
1209 CHECK_WRITING_MODE(aWritingMode);
1210 return mMargin.left;
1211 }
1212 nscoord IEnd(WritingMode aWritingMode) const // inline-end margin
1213 {
1214 CHECK_WRITING_MODE(aWritingMode);
1215 return mMargin.right;
1216 }
1217 nscoord BStart(WritingMode aWritingMode) const // block-start margin
1218 {
1219 CHECK_WRITING_MODE(aWritingMode);
1220 return mMargin.top;
1221 }
1222 nscoord BEnd(WritingMode aWritingMode) const // block-end margin
1223 {
1224 CHECK_WRITING_MODE(aWritingMode);
1225 return mMargin.bottom;
1226 }
1227 nscoord Start(LogicalAxis aAxis, WritingMode aWM) const {
1228 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1229 }
1230 nscoord End(LogicalAxis aAxis, WritingMode aWM) const {
1231 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1232 }
1233
1234 nscoord& IStart(WritingMode aWritingMode) // inline-start margin
1235 {
1236 CHECK_WRITING_MODE(aWritingMode);
1237 return mMargin.left;
1238 }
1239 nscoord& IEnd(WritingMode aWritingMode) // inline-end margin
1240 {
1241 CHECK_WRITING_MODE(aWritingMode);
1242 return mMargin.right;
1243 }
1244 nscoord& BStart(WritingMode aWritingMode) // block-start margin
1245 {
1246 CHECK_WRITING_MODE(aWritingMode);
1247 return mMargin.top;
1248 }
1249 nscoord& BEnd(WritingMode aWritingMode) // block-end margin
1250 {
1251 CHECK_WRITING_MODE(aWritingMode);
1252 return mMargin.bottom;
1253 }
1254 nscoord& Start(LogicalAxis aAxis, WritingMode aWM) {
1255 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1256 }
1257 nscoord& End(LogicalAxis aAxis, WritingMode aWM) {
1258 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1259 }
1260
1261 nscoord IStartEnd(WritingMode aWritingMode) const // inline margins
1262 {
1263 CHECK_WRITING_MODE(aWritingMode);
1264 return mMargin.LeftRight();
1265 }
1266 nscoord BStartEnd(WritingMode aWritingMode) const // block margins
1267 {
1268 CHECK_WRITING_MODE(aWritingMode);
1269 return mMargin.TopBottom();
1270 }
1271 nscoord StartEnd(LogicalAxis aAxis, WritingMode aWM) const {
1272 return aAxis == eLogicalAxisInline ? IStartEnd(aWM) : BStartEnd(aWM);
1273 }
1274
1275 nscoord Side(LogicalSide aSide, WritingMode aWM) const {
1276 switch (aSide) {
1277 case eLogicalSideBStart:
1278 return BStart(aWM);
1279 case eLogicalSideBEnd:
1280 return BEnd(aWM);
1281 case eLogicalSideIStart:
1282 return IStart(aWM);
1283 case eLogicalSideIEnd:
1284 return IEnd(aWM);
1285 }
1286
1287 MOZ_ASSERT_UNREACHABLE("We should handle all sides!");
1288 return BStart(aWM);
1289 }
1290 nscoord& Side(LogicalSide aSide, WritingMode aWM) {
1291 switch (aSide) {
1292 case eLogicalSideBStart:
1293 return BStart(aWM);
1294 case eLogicalSideBEnd:
1295 return BEnd(aWM);
1296 case eLogicalSideIStart:
1297 return IStart(aWM);
1298 case eLogicalSideIEnd:
1299 return IEnd(aWM);
1300 }
1301
1302 MOZ_ASSERT_UNREACHABLE("We should handle all sides!");
1303 return BStart(aWM);
1304 }
1305
1306 /*
1307 * Return margin values for line-relative sides, as defined in
1308 * http://www.w3.org/TR/css-writing-modes-3/#line-directions:
1309 *
1310 * line-left
1311 * Nominally the side from which LTR text would start.
1312 * line-right
1313 * Nominally the side from which RTL text would start. (Opposite of
1314 * line-left.)
1315 */
1316 nscoord LineLeft(WritingMode aWritingMode) const {
1317 // We don't need to CHECK_WRITING_MODE here because the IStart or IEnd
1318 // accessor that we call will do it.
1319 return aWritingMode.IsBidiLTR() ? IStart(aWritingMode) : IEnd(aWritingMode);
1320 }
1321 nscoord LineRight(WritingMode aWritingMode) const {
1322 return aWritingMode.IsBidiLTR() ? IEnd(aWritingMode) : IStart(aWritingMode);
1323 }
1324
1325 /**
1326 * Return a LogicalSize representing the total size of the inline-
1327 * and block-dimension margins.
1328 */
1329 LogicalSize Size(WritingMode aWritingMode) const {
1330 CHECK_WRITING_MODE(aWritingMode);
1331 return LogicalSize(aWritingMode, IStartEnd(), BStartEnd());
1332 }
1333
1334 /**
1335 * Accessors for physical margins, using our writing mode to convert from
1336 * logical values.
1337 */
1338 nscoord Top(WritingMode aWritingMode) const {
1339 CHECK_WRITING_MODE(aWritingMode);
1340 return aWritingMode.IsVertical()
1341 ? (aWritingMode.IsInlineReversed() ? IEnd() : IStart())
1342 : BStart();
1343 }
1344
1345 nscoord Bottom(WritingMode aWritingMode) const {
1346 CHECK_WRITING_MODE(aWritingMode);
1347 return aWritingMode.IsVertical()
1348 ? (aWritingMode.IsInlineReversed() ? IStart() : IEnd())
1349 : BEnd();
1350 }
1351
1352 nscoord Left(WritingMode aWritingMode) const {
1353 CHECK_WRITING_MODE(aWritingMode);
1354 return aWritingMode.IsVertical()
1355 ? (aWritingMode.IsVerticalLR() ? BStart() : BEnd())
1356 : (aWritingMode.IsInlineReversed() ? IEnd() : IStart());
1357 }
1358
1359 nscoord Right(WritingMode aWritingMode) const {
1360 CHECK_WRITING_MODE(aWritingMode);
1361 return aWritingMode.IsVertical()
1362 ? (aWritingMode.IsVerticalLR() ? BEnd() : BStart())
1363 : (aWritingMode.IsInlineReversed() ? IStart() : IEnd());
1364 }
1365
1366 nscoord LeftRight(WritingMode aWritingMode) const {
1367 CHECK_WRITING_MODE(aWritingMode);
1368 return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
1369 }
1370
1371 nscoord TopBottom(WritingMode aWritingMode) const {
1372 CHECK_WRITING_MODE(aWritingMode);
1373 return aWritingMode.IsVertical() ? IStartEnd() : BStartEnd();
1374 }
1375
1376 void SizeTo(WritingMode aWritingMode, nscoord aBStart, nscoord aIEnd,
1377 nscoord aBEnd, nscoord aIStart) {
1378 CHECK_WRITING_MODE(aWritingMode);
1379 mMargin.SizeTo(aBStart, aIEnd, aBEnd, aIStart);
1380 }
1381
1382 /**
1383 * Return an nsMargin containing our physical coordinates
1384 */
1385 nsMargin GetPhysicalMargin(WritingMode aWritingMode) const {
1386 CHECK_WRITING_MODE(aWritingMode);
1387 return aWritingMode.IsVertical()
1388 ? (aWritingMode.IsVerticalLR()
1389 ? (aWritingMode.IsInlineReversed()
1390 ? nsMargin(IEnd(), BEnd(), IStart(), BStart())
1391 : nsMargin(IStart(), BEnd(), IEnd(), BStart()))
1392 : (aWritingMode.IsInlineReversed()
1393 ? nsMargin(IEnd(), BStart(), IStart(), BEnd())
1394 : nsMargin(IStart(), BStart(), IEnd(), BEnd())))
1395 : (aWritingMode.IsInlineReversed()
1396 ? nsMargin(BStart(), IStart(), BEnd(), IEnd())
1397 : nsMargin(BStart(), IEnd(), BEnd(), IStart()));
1398 }
1399
1400 /**
1401 * Return a LogicalMargin representing this margin in a different
1402 * writing mode
1403 */
1404 LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const {
1405 CHECK_WRITING_MODE(aFromMode);
1406 return aToMode == aFromMode
1407 ? *this
1408 : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
1409 }
1410
1411 LogicalMargin& ApplySkipSides(LogicalSides aSkipSides) {
1412 CHECK_WRITING_MODE(aSkipSides.GetWritingMode());
1413 if (aSkipSides.BStart()) {
1414 BStart() = 0;
1415 }
1416 if (aSkipSides.BEnd()) {
1417 BEnd() = 0;
1418 }
1419 if (aSkipSides.IStart()) {
1420 IStart() = 0;
1421 }
1422 if (aSkipSides.IEnd()) {
1423 IEnd() = 0;
1424 }
1425 return *this;
1426 }
1427
1428 bool IsAllZero() const {
1429 return (mMargin.left == 0 && mMargin.top == 0 && mMargin.right == 0 &&
1430 mMargin.bottom == 0);
1431 }
1432
1433 bool operator==(const LogicalMargin& aMargin) const {
1434 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1435 return mMargin == aMargin.mMargin;
1436 }
1437
1438 bool operator!=(const LogicalMargin& aMargin) const {
1439 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1440 return mMargin != aMargin.mMargin;
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 LogicalMargin operator+=(const LogicalMargin& aMargin) {
1451 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1452 mMargin += aMargin.mMargin;
1453 return *this;
1454 }
1455
1456 LogicalMargin operator-(const LogicalMargin& aMargin) const {
1457 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1458 return LogicalMargin(GetWritingMode(), BStart() - aMargin.BStart(),
1459 IEnd() - aMargin.IEnd(), BEnd() - aMargin.BEnd(),
1460 IStart() - aMargin.IStart());
1461 }
1462
1463 friend std::ostream& operator<<(std::ostream& aStream,
1464 const LogicalMargin& aMargin) {
1465 return aStream << aMargin.mMargin;
1466 }
1467
1468 private:
1469 friend class LogicalRect;
1470
1471 LogicalMargin() = delete;
1472
1473 #ifdef DEBUG
1474 WritingMode GetWritingMode() const { return mWritingMode; }
1475 #else
1476 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1477 #endif
1478
1479 nscoord IStart() const // inline-start margin
1480 {
1481 return mMargin.left;
1482 }
1483 nscoord IEnd() const // inline-end margin
1484 {
1485 return mMargin.right;
1486 }
1487 nscoord BStart() const // block-start margin
1488 {
1489 return mMargin.top;
1490 }
1491 nscoord BEnd() const // block-end margin
1492 {
1493 return mMargin.bottom;
1494 }
1495
1496 nscoord& IStart() // inline-start margin
1497 {
1498 return mMargin.left;
1499 }
1500 nscoord& IEnd() // inline-end margin
1501 {
1502 return mMargin.right;
1503 }
1504 nscoord& BStart() // block-start margin
1505 {
1506 return mMargin.top;
1507 }
1508 nscoord& BEnd() // block-end margin
1509 {
1510 return mMargin.bottom;
1511 }
1512
1513 nscoord IStartEnd() const // inline margins
1514 {
1515 return mMargin.LeftRight();
1516 }
1517 nscoord BStartEnd() const // block margins
1518 {
1519 return mMargin.TopBottom();
1520 }
1521
1522 #ifdef DEBUG
1523 WritingMode mWritingMode;
1524 #endif
1525 nsMargin mMargin;
1526 };
1527
1528 /**
1529 * Flow-relative rectangle
1530 */
1531 class LogicalRect {
1532 public:
1533 explicit LogicalRect(WritingMode aWritingMode)
1534 :
1535 #ifdef DEBUG
1536 mWritingMode(aWritingMode),
1537 #endif
1538 mIStart(0),
1539 mBStart(0),
1540 mISize(0),
1541 mBSize(0) {
1542 }
1543
1544 LogicalRect(WritingMode aWritingMode, nscoord aIStart, nscoord aBStart,
1545 nscoord aISize, nscoord aBSize)
1546 :
1547 #ifdef DEBUG
1548 mWritingMode(aWritingMode),
1549 #endif
1550 mIStart(aIStart),
1551 mBStart(aBStart),
1552 mISize(aISize),
1553 mBSize(aBSize) {
1554 }
1555
1556 LogicalRect(WritingMode aWritingMode, const LogicalPoint& aOrigin,
1557 const LogicalSize& aSize)
1558 :
1559 #ifdef DEBUG
1560 mWritingMode(aWritingMode),
1561 #endif
1562 mIStart(aOrigin.mPoint.x),
1563 mBStart(aOrigin.mPoint.y),
1564 mISize(aSize.mSize.width),
1565 mBSize(aSize.mSize.height) {
1566 CHECK_WRITING_MODE(aOrigin.GetWritingMode());
1567 CHECK_WRITING_MODE(aSize.GetWritingMode());
1568 }
1569
1570 LogicalRect(WritingMode aWritingMode, const nsRect& aRect,
1571 const nsSize& aContainerSize)
1572 #ifdef DEBUG
1573 : mWritingMode(aWritingMode)
1574 #endif
1575 {
1576 if (aWritingMode.IsVertical()) {
1577 mBStart = aWritingMode.IsVerticalLR()
1578 ? aRect.X()
1579 : aContainerSize.width - aRect.XMost();
1580 mIStart = aWritingMode.IsInlineReversed()
1581 ? aContainerSize.height - aRect.YMost()
1582 : aRect.Y();
1583 mBSize = aRect.Width();
1584 mISize = aRect.Height();
1585 } else {
1586 mIStart = aWritingMode.IsInlineReversed()
1587 ? aContainerSize.width - aRect.XMost()
1588 : aRect.X();
1589 mBStart = aRect.Y();
1590 mISize = aRect.Width();
1591 mBSize = aRect.Height();
1592 }
1593 }
1594
1595 /**
1596 * Inline- and block-dimension geometry.
1597 */
1598 nscoord IStart(WritingMode aWritingMode) const // inline-start edge
1599 {
1600 CHECK_WRITING_MODE(aWritingMode);
1601 return mIStart;
1602 }
1603 nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
1604 {
1605 CHECK_WRITING_MODE(aWritingMode);
1606 return mIStart + mISize;
1607 }
1608 nscoord ISize(WritingMode aWritingMode) const // inline-size
1609 {
1610 CHECK_WRITING_MODE(aWritingMode);
1611 return mISize;
1612 }
1613
1614 nscoord BStart(WritingMode aWritingMode) const // block-start edge
1615 {
1616 CHECK_WRITING_MODE(aWritingMode);
1617 return mBStart;
1618 }
1619 nscoord BEnd(WritingMode aWritingMode) const // block-end edge
1620 {
1621 CHECK_WRITING_MODE(aWritingMode);
1622 return mBStart + mBSize;
1623 }
1624 nscoord BSize(WritingMode aWritingMode) const // block-size
1625 {
1626 CHECK_WRITING_MODE(aWritingMode);
1627 return mBSize;
1628 }
1629
1630 nscoord Start(LogicalAxis aAxis, WritingMode aWM) const {
1631 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1632 }
1633 nscoord End(LogicalAxis aAxis, WritingMode aWM) const {
1634 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1635 }
1636 nscoord Size(LogicalAxis aAxis, WritingMode aWM) const {
1637 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
1638 }
1639
1640 /**
1641 * Writable (reference) accessors are only available for the basic logical
1642 * fields (Start and Size), not derivatives like End.
1643 */
1644 nscoord& IStart(WritingMode aWritingMode) // inline-start edge
1645 {
1646 CHECK_WRITING_MODE(aWritingMode);
1647 return mIStart;
1648 }
1649 nscoord& ISize(WritingMode aWritingMode) // inline-size
1650 {
1651 CHECK_WRITING_MODE(aWritingMode);
1652 return mISize;
1653 }
1654 nscoord& BStart(WritingMode aWritingMode) // block-start edge
1655 {
1656 CHECK_WRITING_MODE(aWritingMode);
1657 return mBStart;
1658 }
1659 nscoord& BSize(WritingMode aWritingMode) // block-size
1660 {
1661 CHECK_WRITING_MODE(aWritingMode);
1662 return mBSize;
1663 }
1664 nscoord& Start(LogicalAxis aAxis, WritingMode aWM) {
1665 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1666 }
1667 nscoord& Size(LogicalAxis aAxis, WritingMode aWM) {
1668 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
1669 }
1670
1671 /**
1672 * Accessors for line-relative coordinates
1673 */
1674 nscoord LineLeft(WritingMode aWritingMode,
1675 const nsSize& aContainerSize) const {
1676 CHECK_WRITING_MODE(aWritingMode);
1677 if (aWritingMode.IsBidiLTR()) {
1678 return IStart();
1679 }
1680 nscoord containerISize = aWritingMode.IsVertical() ? aContainerSize.height
1681 : aContainerSize.width;
1682 return containerISize - IEnd();
1683 }
1684 nscoord LineRight(WritingMode aWritingMode,
1685 const nsSize& aContainerSize) const {
1686 CHECK_WRITING_MODE(aWritingMode);
1687 if (aWritingMode.IsBidiLTR()) {
1688 return IEnd();
1689 }
1690 nscoord containerISize = aWritingMode.IsVertical() ? aContainerSize.height
1691 : aContainerSize.width;
1692 return containerISize - IStart();
1693 }
1694
1695 /**
1696 * Physical coordinates of the rect.
1697 */
1698 nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const {
1699 CHECK_WRITING_MODE(aWritingMode);
1700 if (aWritingMode.IsVertical()) {
1701 return aWritingMode.IsVerticalLR() ? mBStart : aContainerWidth - BEnd();
1702 }
1703 return aWritingMode.IsInlineReversed() ? aContainerWidth - IEnd() : mIStart;
1704 }
1705
1706 nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const {
1707 CHECK_WRITING_MODE(aWritingMode);
1708 if (aWritingMode.IsVertical()) {
1709 return aWritingMode.IsInlineReversed() ? aContainerHeight - IEnd()
1710 : mIStart;
1711 }
1712 return mBStart;
1713 }
1714
1715 nscoord Width(WritingMode aWritingMode) const {
1716 CHECK_WRITING_MODE(aWritingMode);
1717 return aWritingMode.IsVertical() ? mBSize : mISize;
1718 }
1719
1720 nscoord Height(WritingMode aWritingMode) const {
1721 CHECK_WRITING_MODE(aWritingMode);
1722 return aWritingMode.IsVertical() ? mISize : mBSize;
1723 }
1724
1725 nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const {
1726 CHECK_WRITING_MODE(aWritingMode);
1727 if (aWritingMode.IsVertical()) {
1728 return aWritingMode.IsVerticalLR() ? BEnd() : aContainerWidth - mBStart;
1729 }
1730 return aWritingMode.IsInlineReversed() ? aContainerWidth - mIStart : IEnd();
1731 }
1732
1733 nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const {
1734 CHECK_WRITING_MODE(aWritingMode);
1735 if (aWritingMode.IsVertical()) {
1736 return aWritingMode.IsInlineReversed() ? aContainerHeight - mIStart
1737 : IEnd();
1738 }
1739 return BEnd();
1740 }
1741
1742 bool IsEmpty() const { return mISize <= 0 || mBSize <= 0; }
1743
1744 bool IsAllZero() const {
1745 return (mIStart == 0 && mBStart == 0 && mISize == 0 && mBSize == 0);
1746 }
1747
1748 bool IsZeroSize() const { return (mISize == 0 && mBSize == 0); }
1749
1750 void SetEmpty() { mISize = mBSize = 0; }
1751
1752 bool IsEqualEdges(const LogicalRect aOther) const {
1753 CHECK_WRITING_MODE(aOther.GetWritingMode());
1754 bool result = mIStart == aOther.mIStart && mBStart == aOther.mBStart &&
1755 mISize == aOther.mISize && mBSize == aOther.mBSize;
1756
1757 // We want the same result as nsRect, so assert we get it.
1758 MOZ_ASSERT(result ==
1759 nsRect(mIStart, mBStart, mISize, mBSize)
1760 .IsEqualEdges(nsRect(aOther.mIStart, aOther.mBStart,
1761 aOther.mISize, aOther.mBSize)));
1762 return result;
1763 }
1764
1765 LogicalPoint Origin(WritingMode aWritingMode) const {
1766 CHECK_WRITING_MODE(aWritingMode);
1767 return LogicalPoint(aWritingMode, IStart(), BStart());
1768 }
1769 void SetOrigin(WritingMode aWritingMode, const LogicalPoint& aPoint) {
1770 IStart(aWritingMode) = aPoint.I(aWritingMode);
1771 BStart(aWritingMode) = aPoint.B(aWritingMode);
1772 }
1773
1774 LogicalSize Size(WritingMode aWritingMode) const {
1775 CHECK_WRITING_MODE(aWritingMode);
1776 return LogicalSize(aWritingMode, ISize(), BSize());
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 LogicalRect operator-(const LogicalPoint& aPoint) const {
1793 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1794 return LogicalRect(GetWritingMode(), IStart() - aPoint.I(),
1795 BStart() - aPoint.B(), ISize(), BSize());
1796 }
1797
1798 LogicalRect& operator-=(const LogicalPoint& aPoint) {
1799 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1800 mIStart -= aPoint.mPoint.x;
1801 mBStart -= aPoint.mPoint.y;
1802 return *this;
1803 }
1804
1805 void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta) {
1806 CHECK_WRITING_MODE(aWritingMode);
1807 CHECK_WRITING_MODE(aDelta.GetWritingMode());
1808 IStart() += aDelta.I();
1809 BStart() += aDelta.B();
1810 }
1811
1812 void Inflate(nscoord aD) {
1813 #ifdef DEBUG
1814 // Compute using nsRect and assert the results match
1815 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1816 rectDebug.Inflate(aD);
1817 #endif
1818 mIStart -= aD;
1819 mBStart -= aD;
1820 mISize += 2 * aD;
1821 mBSize += 2 * aD;
1822 MOZ_ASSERT(
1823 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1824 }
1825 void Inflate(nscoord aDI, nscoord aDB) {
1826 #ifdef DEBUG
1827 // Compute using nsRect and assert the results match
1828 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1829 rectDebug.Inflate(aDI, aDB);
1830 #endif
1831 mIStart -= aDI;
1832 mBStart -= aDB;
1833 mISize += 2 * aDI;
1834 mBSize += 2 * aDB;
1835 MOZ_ASSERT(
1836 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1837 }
1838 void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin) {
1839 CHECK_WRITING_MODE(aWritingMode);
1840 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1841 #ifdef DEBUG
1842 // Compute using nsRect and assert the results match
1843 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1844 rectDebug.Inflate(aMargin.mMargin);
1845 #endif
1846 mIStart -= aMargin.mMargin.left;
1847 mBStart -= aMargin.mMargin.top;
1848 mISize += aMargin.mMargin.LeftRight();
1849 mBSize += aMargin.mMargin.TopBottom();
1850 MOZ_ASSERT(
1851 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1852 }
1853
1854 void Deflate(nscoord aD) {
1855 #ifdef DEBUG
1856 // Compute using nsRect and assert the results match
1857 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1858 rectDebug.Deflate(aD);
1859 #endif
1860 mIStart += aD;
1861 mBStart += aD;
1862 mISize = std::max(0, mISize - 2 * aD);
1863 mBSize = std::max(0, mBSize - 2 * aD);
1864 MOZ_ASSERT(
1865 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1866 }
1867 void Deflate(nscoord aDI, nscoord aDB) {
1868 #ifdef DEBUG
1869 // Compute using nsRect and assert the results match
1870 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1871 rectDebug.Deflate(aDI, aDB);
1872 #endif
1873 mIStart += aDI;
1874 mBStart += aDB;
1875 mISize = std::max(0, mISize - 2 * aDI);
1876 mBSize = std::max(0, mBSize - 2 * aDB);
1877 MOZ_ASSERT(
1878 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1879 }
1880 void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin) {
1881 CHECK_WRITING_MODE(aWritingMode);
1882 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1883 #ifdef DEBUG
1884 // Compute using nsRect and assert the results match
1885 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1886 rectDebug.Deflate(aMargin.mMargin);
1887 #endif
1888 mIStart += aMargin.mMargin.left;
1889 mBStart += aMargin.mMargin.top;
1890 mISize = std::max(0, mISize - aMargin.mMargin.LeftRight());
1891 mBSize = std::max(0, mBSize - aMargin.mMargin.TopBottom());
1892 MOZ_ASSERT(
1893 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1894 }
1895
1896 /**
1897 * Return an nsRect containing our physical coordinates within the given
1898 * container size.
1899 */
1900 nsRect GetPhysicalRect(WritingMode aWritingMode,
1901 const nsSize& aContainerSize) const {
1902 CHECK_WRITING_MODE(aWritingMode);
1903 if (aWritingMode.IsVertical()) {
1904 return nsRect(aWritingMode.IsVerticalLR() ? BStart()
1905 : aContainerSize.width - BEnd(),
1906 aWritingMode.IsInlineReversed()
1907 ? aContainerSize.height - IEnd()
1908 : IStart(),
1909 BSize(), ISize());
1910 } else {
1911 return nsRect(aWritingMode.IsInlineReversed()
1912 ? aContainerSize.width - IEnd()
1913 : IStart(),
1914 BStart(), ISize(), BSize());
1915 }
1916 }
1917
1918 /**
1919 * Return a LogicalRect representing this rect in a different writing mode
1920 */
1921 LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode,
1922 const nsSize& aContainerSize) const {
1923 CHECK_WRITING_MODE(aFromMode);
1924 return aToMode == aFromMode
1925 ? *this
1926 : LogicalRect(aToMode,
1927 GetPhysicalRect(aFromMode, aContainerSize),
1928 aContainerSize);
1929 }
1930
1931 /**
1932 * Set *this to be the rectangle containing the intersection of aRect1
1933 * and aRect2, return whether the intersection is non-empty.
1934 */
1935 bool IntersectRect(const LogicalRect& aRect1, const LogicalRect& aRect2) {
1936 CHECK_WRITING_MODE(aRect1.mWritingMode);
1937 CHECK_WRITING_MODE(aRect2.mWritingMode);
1938 #ifdef DEBUG
1939 // Compute using nsRect and assert the results match
1940 nsRect rectDebug;
1941 rectDebug.IntersectRect(
1942 nsRect(aRect1.mIStart, aRect1.mBStart, aRect1.mISize, aRect1.mBSize),
1943 nsRect(aRect2.mIStart, aRect2.mBStart, aRect2.mISize, aRect2.mBSize));
1944 #endif
1945
1946 nscoord iEnd = std::min(aRect1.IEnd(), aRect2.IEnd());
1947 mIStart = std::max(aRect1.mIStart, aRect2.mIStart);
1948 mISize = iEnd - mIStart;
1949
1950 nscoord bEnd = std::min(aRect1.BEnd(), aRect2.BEnd());
1951 mBStart = std::max(aRect1.mBStart, aRect2.mBStart);
1952 mBSize = bEnd - mBStart;
1953
1954 if (mISize < 0 || mBSize < 0) {
1955 mISize = 0;
1956 mBSize = 0;
1957 }
1958
1959 MOZ_ASSERT(
1960 (rectDebug.IsEmpty() && (mISize == 0 || mBSize == 0)) ||
1961 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1962 return mISize > 0 && mBSize > 0;
1963 }
1964
1965 friend std::ostream& operator<<(std::ostream& aStream,
1966 const LogicalRect& aRect) {
1967 return aStream << '(' << aRect.IStart() << ',' << aRect.BStart() << ','
1968 << aRect.ISize() << ',' << aRect.BSize() << ')';
1969 }
1970
1971 private:
1972 LogicalRect() = delete;
1973
1974 #ifdef DEBUG
1975 WritingMode GetWritingMode() const { return mWritingMode; }
1976 #else
1977 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1978 #endif
1979
1980 nscoord IStart() const // inline-start edge
1981 {
1982 return mIStart;
1983 }
1984 nscoord IEnd() const // inline-end edge
1985 {
1986 return mIStart + mISize;
1987 }
1988 nscoord ISize() const // inline-size
1989 {
1990 return mISize;
1991 }
1992
1993 nscoord BStart() const // block-start edge
1994 {
1995 return mBStart;
1996 }
1997 nscoord BEnd() const // block-end edge
1998 {
1999 return mBStart + mBSize;
2000 }
2001 nscoord BSize() const // block-size
2002 {
2003 return mBSize;
2004 }
2005
2006 nscoord& IStart() // inline-start edge
2007 {
2008 return mIStart;
2009 }
2010 nscoord& ISize() // inline-size
2011 {
2012 return mISize;
2013 }
2014 nscoord& BStart() // block-start edge
2015 {
2016 return mBStart;
2017 }
2018 nscoord& BSize() // block-size
2019 {
2020 return mBSize;
2021 }
2022
2023 #ifdef DEBUG
2024 WritingMode mWritingMode;
2025 #endif
2026 // Inline- and block-geometry dimension
2027 nscoord mIStart; // inline-start edge
2028 nscoord mBStart; // block-start edge
2029 nscoord mISize; // inline-size
2030 nscoord mBSize; // block-size
2031 };
2032
2033 template <typename T>
2034 const T& StyleRect<T>::Get(WritingMode aWM, LogicalSide aSide) const {
2035 return Get(aWM.PhysicalSide(aSide));
2036 }
2037
2038 template <typename T>
2039 const T& StyleRect<T>::GetIStart(WritingMode aWM) const {
2040 return Get(aWM, eLogicalSideIStart);
2041 }
2042
2043 template <typename T>
2044 const T& StyleRect<T>::GetBStart(WritingMode aWM) const {
2045 return Get(aWM, eLogicalSideBStart);
2046 }
2047
2048 template <typename T>
2049 const T& StyleRect<T>::GetIEnd(WritingMode aWM) const {
2050 return Get(aWM, eLogicalSideIEnd);
2051 }
2052
2053 template <typename T>
2054 const T& StyleRect<T>::GetBEnd(WritingMode aWM) const {
2055 return Get(aWM, eLogicalSideBEnd);
2056 }
2057
2058 template <typename T>
2059 T& StyleRect<T>::Get(WritingMode aWM, LogicalSide aSide) {
2060 return Get(aWM.PhysicalSide(aSide));
2061 }
2062
2063 template <typename T>
2064 T& StyleRect<T>::GetIStart(WritingMode aWM) {
2065 return Get(aWM, eLogicalSideIStart);
2066 }
2067
2068 template <typename T>
2069 T& StyleRect<T>::GetBStart(WritingMode aWM) {
2070 return Get(aWM, eLogicalSideBStart);
2071 }
2072
2073 template <typename T>
2074 T& StyleRect<T>::GetIEnd(WritingMode aWM) {
2075 return Get(aWM, eLogicalSideIEnd);
2076 }
2077
2078 template <typename T>
2079 T& StyleRect<T>::GetBEnd(WritingMode aWM) {
2080 return Get(aWM, eLogicalSideBEnd);
2081 }
2082
2083 template <typename T>
2084 const T& StyleRect<T>::Start(mozilla::LogicalAxis aAxis,
2085 mozilla::WritingMode aWM) const {
2086 return Get(aWM, aAxis == mozilla::eLogicalAxisInline
2087 ? mozilla::eLogicalSideIStart
2088 : mozilla::eLogicalSideBStart);
2089 }
2090
2091 template <typename T>
2092 const T& StyleRect<T>::End(mozilla::LogicalAxis aAxis,
2093 mozilla::WritingMode aWM) const {
2094 return Get(aWM, aAxis == mozilla::eLogicalAxisInline
2095 ? mozilla::eLogicalSideIEnd
2096 : mozilla::eLogicalSideBEnd);
2097 }
2098
2099 inline AspectRatio AspectRatio::ConvertToWritingMode(
2100 const WritingMode& aWM) const {
2101 return aWM.IsVertical() ? Inverted() : *this;
2102 }
2103
2104 } // namespace mozilla
2105
2106 // Definitions of inline methods for nsStylePosition, declared in
2107 // nsStyleStruct.h but not defined there because they need WritingMode.
2108 inline const mozilla::StyleSize& nsStylePosition::ISize(WritingMode aWM) const {
2109 return aWM.IsVertical() ? mHeight : mWidth;
2110 }
2111 inline const mozilla::StyleSize& nsStylePosition::MinISize(
2112 WritingMode aWM) const {
2113 return aWM.IsVertical() ? mMinHeight : mMinWidth;
2114 }
2115 inline const mozilla::StyleMaxSize& nsStylePosition::MaxISize(
2116 WritingMode aWM) const {
2117 return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
2118 }
2119 inline const mozilla::StyleSize& nsStylePosition::BSize(WritingMode aWM) const {
2120 return aWM.IsVertical() ? mWidth : mHeight;
2121 }
2122 inline const mozilla::StyleSize& nsStylePosition::MinBSize(
2123 WritingMode aWM) const {
2124 return aWM.IsVertical() ? mMinWidth : mMinHeight;
2125 }
2126 inline const mozilla::StyleMaxSize& nsStylePosition::MaxBSize(
2127 WritingMode aWM) const {
2128 return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
2129 }
2130 inline const mozilla::StyleSize& nsStylePosition::Size(
2131 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2132 return aAxis == mozilla::eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
2133 }
2134 inline const mozilla::StyleSize& nsStylePosition::MinSize(
2135 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2136 return aAxis == mozilla::eLogicalAxisInline ? MinISize(aWM) : MinBSize(aWM);
2137 }
2138 inline const mozilla::StyleMaxSize& nsStylePosition::MaxSize(
2139 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2140 return aAxis == mozilla::eLogicalAxisInline ? MaxISize(aWM) : MaxBSize(aWM);
2141 }
2142
2143 inline bool nsStylePosition::ISizeDependsOnContainer(WritingMode aWM) const {
2144 const auto& iSize = ISize(aWM);
2145 return iSize.IsAuto() || ISizeCoordDependsOnContainer(iSize);
2146 }
2147 inline bool nsStylePosition::MinISizeDependsOnContainer(WritingMode aWM) const {
2148 // NOTE: For a flex item, "min-inline-size:auto" is supposed to behave like
2149 // "min-content", which does depend on the container, so you might think we'd
2150 // need a special case for "flex item && min-inline-size:auto" here. However,
2151 // we don't actually need that special-case code, because flex items are
2152 // explicitly supposed to *ignore* their min-inline-size (i.e. behave like
2153 // it's 0) until the flex container explicitly considers it. So -- since the
2154 // flex container doesn't rely on this method, we don't need to worry about
2155 // special behavior for flex items' "min-inline-size:auto" values here.
2156 return ISizeCoordDependsOnContainer(MinISize(aWM));
2157 }
2158 inline bool nsStylePosition::MaxISizeDependsOnContainer(WritingMode aWM) const {
2159 // NOTE: The comment above MinISizeDependsOnContainer about flex items
2160 // applies here, too.
2161 return ISizeCoordDependsOnContainer(MaxISize(aWM));
2162 }
2163 // Note that these functions count `auto` as depending on the container
2164 // since that's the case for absolutely positioned elements.
2165 // However, some callers do not care about this case and should check
2166 // for it, since it is the most common case.
2167 // FIXME: We should probably change the assumption to be the other way
2168 // around.
2169 inline bool nsStylePosition::BSizeDependsOnContainer(WritingMode aWM) const {
2170 const auto& bSize = BSize(aWM);
2171 return bSize.BehavesLikeInitialValueOnBlockAxis() ||
2172 BSizeCoordDependsOnContainer(bSize);
2173 }
2174 inline bool nsStylePosition::MinBSizeDependsOnContainer(WritingMode aWM) const {
2175 return BSizeCoordDependsOnContainer(MinBSize(aWM));
2176 }
2177 inline bool nsStylePosition::MaxBSizeDependsOnContainer(WritingMode aWM) const {
2178 return BSizeCoordDependsOnContainer(MaxBSize(aWM));
2179 }
2180
2181 inline bool nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const {
2182 return mMargin.GetBStart(aWM).IsAuto() || mMargin.GetBEnd(aWM).IsAuto();
2183 }
2184
2185 inline bool nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const {
2186 return mMargin.GetIStart(aWM).IsAuto() || mMargin.GetIEnd(aWM).IsAuto();
2187 }
2188 inline bool nsStyleMargin::HasAuto(mozilla::LogicalAxis aAxis,
2189 mozilla::WritingMode aWM) const {
2190 return aAxis == mozilla::eLogicalAxisInline ? HasInlineAxisAuto(aWM)
2191 : HasBlockAxisAuto(aWM);
2192 }
2193
2194 inline mozilla::StyleAlignFlags nsStylePosition::UsedSelfAlignment(
2195 mozilla::LogicalAxis aAxis, const mozilla::ComputedStyle* aParent) const {
2196 return aAxis == mozilla::eLogicalAxisBlock ? UsedAlignSelf(aParent)._0
2197 : UsedJustifySelf(aParent)._0;
2198 }
2199
2200 inline mozilla::StyleContentDistribution nsStylePosition::UsedContentAlignment(
2201 mozilla::LogicalAxis aAxis) const {
2202 return aAxis == mozilla::eLogicalAxisBlock ? mAlignContent : mJustifyContent;
2203 }
2204
2205 inline mozilla::StyleContentDistribution nsStylePosition::UsedTracksAlignment(
2206 mozilla::LogicalAxis aAxis, uint32_t aIndex) const {
2207 using T = mozilla::StyleAlignFlags;
2208 const auto& tracksAlignment =
2209 aAxis == mozilla::eLogicalAxisBlock ? mAlignTracks : mJustifyTracks;
2210 if (MOZ_LIKELY(tracksAlignment.IsEmpty())) {
2211 // An empty array encodes the initial value, 'normal', which behaves as
2212 // 'start' for Grid containers.
2213 return mozilla::StyleContentDistribution{T::START};
2214 }
2215
2216 // If there are fewer values than tracks, then the last value is used for all
2217 // the remaining tracks.
2218 const auto& ta = tracksAlignment.AsSpan();
2219 auto align = ta[std::min<size_t>(aIndex, ta.Length() - 1)];
2220 if (align.primary == T::NORMAL) {
2221 align = mozilla::StyleContentDistribution{T::START};
2222 }
2223 return align;
2224 }
2225
2226 #endif // WritingModes_h_
2227