Coverage Report

Created: 2018-09-25 14:53

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