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