tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WritingModes.h (81280B)


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