Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/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
{
51
  return aAxis == eLogicalAxisBlock ? eLogicalAxisInline : eLogicalAxisBlock;
52
}
53
54
0
inline bool IsInline(LogicalSide aSide) { return aSide & 0x2; }
55
0
inline bool IsBlock(LogicalSide aSide) { return !IsInline(aSide); }
56
0
inline bool IsEnd(LogicalSide aSide) { return aSide & 0x1; }
57
0
inline bool IsStart(LogicalSide aSide) { return !IsEnd(aSide); }
58
59
inline LogicalAxis GetAxis(LogicalSide aSide)
60
{
61
  return IsInline(aSide) ? eLogicalAxisInline : eLogicalAxisBlock;
62
}
63
64
inline LogicalEdge GetEdge(LogicalSide aSide)
65
0
{
66
0
  return IsEnd(aSide) ? eLogicalEdgeEnd : eLogicalEdgeStart;
67
0
}
68
69
inline LogicalEdge GetOppositeEdge(LogicalEdge aEdge)
70
{
71
  // This relies on the only two LogicalEdge enum values being 0 and 1.
72
  return LogicalEdge(1 - aEdge);
73
}
74
75
inline LogicalSide
76
MakeLogicalSide(LogicalAxis aAxis, LogicalEdge aEdge)
77
0
{
78
0
  return LogicalSide((aAxis << 1) | aEdge);
79
0
}
80
81
inline LogicalSide GetOppositeSide(LogicalSide aSide)
82
{
83
  return MakeLogicalSide(GetAxis(aSide), GetOppositeEdge(GetEdge(aSide)));
84
}
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
0
  LogicalSides() : mBits(0) {}
109
  explicit LogicalSides(LogicalSideBits aSideBits)
110
  {
111
    MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
112
    mBits = aSideBits;
113
  }
114
0
  bool IsEmpty() const { return mBits == 0; }
115
0
  bool BStart()  const { return mBits & eLogicalSideBitsBStart; }
116
0
  bool BEnd()    const { return mBits & eLogicalSideBitsBEnd; }
117
0
  bool IStart()  const { return mBits & eLogicalSideBitsIStart; }
118
0
  bool IEnd()    const { return mBits & eLogicalSideBitsIEnd; }
119
  bool Contains(LogicalSideBits aSideBits) const
120
0
  {
121
0
    MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
122
0
    return (mBits & aSideBits) == aSideBits;
123
0
  }
124
  LogicalSides operator|(LogicalSides aOther) const
125
  {
126
    return LogicalSides(LogicalSideBits(mBits | aOther.mBits));
127
  }
128
  LogicalSides operator|(LogicalSideBits aSideBits) const
129
0
  {
130
0
    return *this | LogicalSides(aSideBits);
131
0
  }
132
  LogicalSides& operator|=(LogicalSides aOther)
133
0
  {
134
0
    mBits |= aOther.mBits;
135
0
    return *this;
136
0
  }
137
  LogicalSides& operator|=(LogicalSideBits aSideBits)
138
0
  {
139
0
    return *this |= LogicalSides(aSideBits);
140
0
  }
141
  bool operator==(LogicalSides aOther) const
142
0
  {
143
0
    return mBits == aOther.mBits;
144
0
  }
145
  bool operator!=(LogicalSides aOther) const
146
0
  {
147
0
    return !(*this == aOther);
148
0
  }
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
0
  InlineDir GetInlineDir() const { return InlineDir(mWritingMode & eInlineMask); }
206
207
  /**
208
   * Return the absolute block flow direction as a BlockDir
209
   */
210
0
  BlockDir GetBlockDir() const { return BlockDir(mWritingMode & eBlockMask); }
211
212
  /**
213
   * Return the line-relative inline flow direction as a BidiDir
214
   */
215
0
  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
0
  bool IsInlineReversed() const { return !!(mWritingMode & eInlineFlowMask); }
224
225
  /**
226
   * Return true if bidi direction is LTR. (Convenience method)
227
   */
228
0
  bool IsBidiLTR() const { return eBidiLTR == GetBidiDir(); }
229
230
  /**
231
   * True if vertical-mode block direction is LR (convenience method).
232
   */
233
0
  bool IsVerticalLR() const { return eBlockLR == GetBlockDir(); }
234
235
  /**
236
   * True if vertical-mode block direction is RL (convenience method).
237
   */
238
  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
0
  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
0
  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
0
  {
260
0
    return IsLineInverted() ? -1 : 1;
261
0
  }
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
0
  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
0
  bool IsCentralBaseline() const { return IsVertical() && !IsSideways(); }
284
285
  /**
286
   * Return true if boxes with this writing mode should use alphabetical
287
   * baselines.
288
   */
289
0
  bool IsAlphabeticalBaseline() const { return !IsCentralBaseline(); }
290
291
292
  static mozilla::PhysicalAxis PhysicalAxisForLogicalAxis(
293
                                              uint8_t aWritingModeValue,
294
                                              LogicalAxis aAxis)
295
0
  {
296
0
    // This relies on bit 0 of a writing-value mode indicating vertical
297
0
    // orientation and bit 0 of a LogicalAxis value indicating the inline axis,
298
0
    // so that it can correctly form mozilla::PhysicalAxis values using bit
299
0
    // manipulation.
300
0
    static_assert(NS_STYLE_WRITING_MODE_HORIZONTAL_TB == 0 &&
301
0
                  NS_STYLE_WRITING_MODE_VERTICAL_RL == 1 &&
302
0
                  NS_STYLE_WRITING_MODE_VERTICAL_LR == 3 &&
303
0
                  eLogicalAxisBlock == 0 &&
304
0
                  eLogicalAxisInline == 1 &&
305
0
                  eAxisVertical == 0 &&
306
0
                  eAxisHorizontal == 1,
307
0
                  "unexpected writing-mode, logical axis or physical axis "
308
0
                  "constant values");
309
0
    return mozilla::PhysicalAxis((aWritingModeValue ^ aAxis) & 0x1);
310
0
  }
311
312
  mozilla::PhysicalAxis PhysicalAxis(LogicalAxis aAxis) const
313
0
  {
314
0
    // This will set wm to either NS_STYLE_WRITING_MODE_HORIZONTAL_TB or
315
0
    // NS_STYLE_WRITING_MODE_VERTICAL_RL, and not the other two (real
316
0
    // and hypothetical) values.  But this is fine; we only need to
317
0
    // distinguish between vertical and horizontal in
318
0
    // PhysicalAxisForLogicalAxis.
319
0
    int wm = mWritingMode & eOrientationMask;
320
0
    return PhysicalAxisForLogicalAxis(wm, aAxis);
321
0
  }
322
323
  static mozilla::Side PhysicalSideForBlockAxis(uint8_t aWritingModeValue,
324
                                                LogicalEdge aEdge)
325
0
  {
326
0
    // indexes are NS_STYLE_WRITING_MODE_* values, which are the same as these
327
0
    // two-bit values:
328
0
    //   bit 0 = the eOrientationMask value
329
0
    //   bit 1 = the eBlockFlowMask value
330
0
    static const mozilla::Side kLogicalBlockSides[][2] = {
331
0
      { eSideTop,    eSideBottom },  // horizontal-tb
332
0
      { eSideRight,  eSideLeft   },  // vertical-rl
333
0
      { eSideBottom, eSideTop    },  // (horizontal-bt)
334
0
      { eSideLeft,   eSideRight  },  // vertical-lr
335
0
    };
336
0
337
0
    // Ignore the SIDEWAYS_MASK bit of the writing-mode value, as this has no
338
0
    // effect on the side mappings.
339
0
    aWritingModeValue &= ~NS_STYLE_WRITING_MODE_SIDEWAYS_MASK;
340
0
341
0
    // What's left of the writing-mode should be in the range 0-3:
342
0
    NS_ASSERTION(aWritingModeValue < 4, "invalid aWritingModeValue value");
343
0
344
0
    return kLogicalBlockSides[aWritingModeValue][aEdge];
345
0
  }
346
347
  mozilla::Side PhysicalSideForInlineAxis(LogicalEdge aEdge) const
348
0
  {
349
0
    // indexes are four-bit values:
350
0
    //   bit 0 = the eOrientationMask value
351
0
    //   bit 1 = the eInlineFlowMask value
352
0
    //   bit 2 = the eBlockFlowMask value
353
0
    //   bit 3 = the eLineOrientMask value
354
0
    // Not all of these combinations can actually be specified via CSS: there
355
0
    // is no horizontal-bt writing-mode, and no text-orientation value that
356
0
    // produces "inverted" text. (The former 'sideways-left' value, no longer
357
0
    // in the spec, would have produced this in vertical-rl mode.)
358
0
    static const mozilla::Side kLogicalInlineSides[][2] = {
359
0
      { eSideLeft,   eSideRight  },  // horizontal-tb               ltr
360
0
      { eSideTop,    eSideBottom },  // vertical-rl                 ltr
361
0
      { eSideRight,  eSideLeft   },  // horizontal-tb               rtl
362
0
      { eSideBottom, eSideTop    },  // vertical-rl                 rtl
363
0
      { eSideRight,  eSideLeft   },  // (horizontal-bt)  (inverted) ltr
364
0
      { eSideTop,    eSideBottom },  // sideways-lr                 rtl
365
0
      { eSideLeft,   eSideRight  },  // (horizontal-bt)  (inverted) rtl
366
0
      { eSideBottom, eSideTop    },  // sideways-lr                 ltr
367
0
      { eSideLeft,   eSideRight  },  // horizontal-tb    (inverted) rtl
368
0
      { eSideTop,    eSideBottom },  // vertical-rl      (inverted) rtl
369
0
      { eSideRight,  eSideLeft   },  // horizontal-tb    (inverted) ltr
370
0
      { eSideBottom, eSideTop    },  // vertical-rl      (inverted) ltr
371
0
      { eSideLeft,   eSideRight  },  // (horizontal-bt)             ltr
372
0
      { eSideTop,    eSideBottom },  // vertical-lr                 ltr
373
0
      { eSideRight,  eSideLeft   },  // (horizontal-bt)             rtl
374
0
      { eSideBottom, eSideTop    },  // vertical-lr                 rtl
375
0
    };
376
0
377
0
    // Inline axis sides depend on all three of writing-mode, text-orientation
378
0
    // and direction, which are encoded in the eOrientationMask,
379
0
    // eInlineFlowMask, eBlockFlowMask and eLineOrientMask bits.  Use these four
380
0
    // bits to index into kLogicalInlineSides.
381
0
    static_assert(eOrientationMask == 0x01 && eInlineFlowMask == 0x02 &&
382
0
                  eBlockFlowMask == 0x04 && eLineOrientMask == 0x08,
383
0
                  "unexpected mask values");
384
0
    int index = mWritingMode & 0x0F;
385
0
    return kLogicalInlineSides[index][aEdge];
386
0
  }
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
0
  {
394
0
    if (IsBlock(aSide)) {
395
0
      static_assert(eOrientationMask == 0x01 && eBlockFlowMask == 0x04,
396
0
                    "unexpected mask values");
397
0
      int wm = ((mWritingMode & eBlockFlowMask) >> 1) |
398
0
               (mWritingMode & eOrientationMask);
399
0
      return PhysicalSideForBlockAxis(wm, GetEdge(aSide));
400
0
    }
401
0
402
0
    return PhysicalSideForInlineAxis(GetEdge(aSide));
403
0
  }
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
0
  {
412
0
    // indexes are four-bit values:
413
0
    //   bit 0 = the eOrientationMask value
414
0
    //   bit 1 = the eInlineFlowMask value
415
0
    //   bit 2 = the eBlockFlowMask value
416
0
    //   bit 3 = the eLineOrientMask value
417
0
    static const LogicalSide kPhysicalToLogicalSides[][4] = {
418
0
      // top                right
419
0
      // bottom             left
420
0
      { eLogicalSideBStart, eLogicalSideIEnd,
421
0
        eLogicalSideBEnd,   eLogicalSideIStart },  // horizontal-tb         ltr
422
0
      { eLogicalSideIStart, eLogicalSideBStart,
423
0
        eLogicalSideIEnd,   eLogicalSideBEnd   },  // vertical-rl           ltr
424
0
      { eLogicalSideBStart, eLogicalSideIStart,
425
0
        eLogicalSideBEnd,   eLogicalSideIEnd   },  // horizontal-tb         rtl
426
0
      { eLogicalSideIEnd,   eLogicalSideBStart,
427
0
        eLogicalSideIStart, eLogicalSideBEnd   },  // vertical-rl           rtl
428
0
      { eLogicalSideBEnd,   eLogicalSideIStart,
429
0
        eLogicalSideBStart, eLogicalSideIEnd   },  // (horizontal-bt) (inv) ltr
430
0
      { eLogicalSideIStart, eLogicalSideBEnd,
431
0
        eLogicalSideIEnd,   eLogicalSideBStart },  // vertical-lr   sw-left rtl
432
0
      { eLogicalSideBEnd,   eLogicalSideIEnd,
433
0
        eLogicalSideBStart, eLogicalSideIStart },  // (horizontal-bt) (inv) rtl
434
0
      { eLogicalSideIEnd,   eLogicalSideBEnd,
435
0
        eLogicalSideIStart, eLogicalSideBStart },  // vertical-lr   sw-left ltr
436
0
      { eLogicalSideBStart, eLogicalSideIEnd,
437
0
        eLogicalSideBEnd,   eLogicalSideIStart },  // horizontal-tb   (inv) rtl
438
0
      { eLogicalSideIStart, eLogicalSideBStart,
439
0
        eLogicalSideIEnd,   eLogicalSideBEnd   },  // vertical-rl   sw-left rtl
440
0
      { eLogicalSideBStart, eLogicalSideIStart,
441
0
        eLogicalSideBEnd,   eLogicalSideIEnd   },  // horizontal-tb   (inv) ltr
442
0
      { eLogicalSideIEnd,   eLogicalSideBStart,
443
0
        eLogicalSideIStart, eLogicalSideBEnd   },  // vertical-rl   sw-left ltr
444
0
      { eLogicalSideBEnd,   eLogicalSideIEnd,
445
0
        eLogicalSideBStart, eLogicalSideIStart },  // (horizontal-bt)       ltr
446
0
      { eLogicalSideIStart, eLogicalSideBEnd,
447
0
        eLogicalSideIEnd,   eLogicalSideBStart },  // vertical-lr           ltr
448
0
      { eLogicalSideBEnd,   eLogicalSideIStart,
449
0
        eLogicalSideBStart, eLogicalSideIEnd   },  // (horizontal-bt)       rtl
450
0
      { eLogicalSideIEnd,   eLogicalSideBEnd,
451
0
        eLogicalSideIStart, eLogicalSideBStart },  // vertical-lr           rtl
452
0
    };
453
0
454
0
    static_assert(eOrientationMask == 0x01 && eInlineFlowMask == 0x02 &&
455
0
                  eBlockFlowMask == 0x04 && eLineOrientMask == 0x08,
456
0
                  "unexpected mask values");
457
0
    int index = mWritingMode & 0x0F;
458
0
    return kPhysicalToLogicalSides[index][aSide];
459
0
  }
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
  {
467
    auto side = static_cast<LogicalSide>(aDir);
468
    if (IsInline(side)) {
469
      return IsBidiLTR() ? side : GetOppositeSide(side);
470
    }
471
    return !IsLineInverted() ? side : GetOppositeSide(side);
472
  }
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
0
  { }
482
483
  /**
484
   * Construct writing mode based on a ComputedStyle.
485
   */
486
  explicit WritingMode(ComputedStyle* aComputedStyle)
487
0
  {
488
0
    NS_ASSERTION(aComputedStyle, "we need an ComputedStyle here");
489
0
    InitFromStyleVisibility(aComputedStyle->StyleVisibility());
490
0
  }
491
492
  explicit WritingMode(const nsStyleVisibility* aStyleVisibility)
493
0
  {
494
0
    NS_ASSERTION(aStyleVisibility, "we need an nsStyleVisibility here");
495
0
    InitFromStyleVisibility(aStyleVisibility);
496
0
  }
497
498
private:
499
  void InitFromStyleVisibility(const nsStyleVisibility* aStyleVisibility)
500
0
  {
501
0
    switch (aStyleVisibility->mWritingMode) {
502
0
      case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
503
0
        mWritingMode = 0;
504
0
        break;
505
0
506
0
      case NS_STYLE_WRITING_MODE_VERTICAL_LR:
507
0
      {
508
0
        mWritingMode = eBlockFlowMask |
509
0
                       eLineOrientMask |
510
0
                       eOrientationMask;
511
0
        uint8_t textOrientation = aStyleVisibility->mTextOrientation;
512
0
        if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS) {
513
0
          mWritingMode |= eSidewaysMask;
514
0
        }
515
0
        break;
516
0
      }
517
0
518
0
      case NS_STYLE_WRITING_MODE_VERTICAL_RL:
519
0
      {
520
0
        mWritingMode = eOrientationMask;
521
0
        uint8_t textOrientation = aStyleVisibility->mTextOrientation;
522
0
        if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS) {
523
0
          mWritingMode |= eSidewaysMask;
524
0
        }
525
0
        break;
526
0
      }
527
0
528
0
      case NS_STYLE_WRITING_MODE_SIDEWAYS_LR:
529
0
        mWritingMode = eBlockFlowMask |
530
0
                       eInlineFlowMask |
531
0
                       eOrientationMask |
532
0
                       eSidewaysMask;
533
0
        break;
534
0
535
0
      case NS_STYLE_WRITING_MODE_SIDEWAYS_RL:
536
0
        mWritingMode = eOrientationMask |
537
0
                       eSidewaysMask;
538
0
        break;
539
0
540
0
      default:
541
0
        MOZ_ASSERT_UNREACHABLE("unknown writing mode!");
542
0
        mWritingMode = 0;
543
0
        break;
544
0
    }
545
0
546
0
    if (NS_STYLE_DIRECTION_RTL == aStyleVisibility->mDirection) {
547
0
      mWritingMode ^= eInlineFlowMask | eBidiMask;
548
0
    }
549
0
  }
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
0
  {
572
0
    if (IS_LEVEL_RTL(level) == IsBidiLTR()) {
573
0
      mWritingMode ^= eBidiMask | eInlineFlowMask;
574
0
    }
575
0
  }
576
577
  /**
578
   * Compare two WritingModes for equality.
579
   */
580
  bool operator==(const WritingMode& aOther) const
581
  {
582
    return mWritingMode == aOther.mWritingMode;
583
  }
584
585
  bool operator!=(const WritingMode& aOther) const
586
0
  {
587
0
    return mWritingMode != aOther.mWritingMode;
588
0
  }
589
590
  /**
591
   * Check whether two modes are orthogonal to each other.
592
   */
593
  bool IsOrthogonalTo(const WritingMode& aOther) const
594
0
  {
595
0
    return IsVertical() != aOther.IsVertical();
596
0
  }
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
  {
610
    mozilla::Side myStartSide =
611
      this->PhysicalSide(MakeLogicalSide(aLogicalAxis,
612
                                         eLogicalEdgeStart));
613
614
    // Figure out which of aOther's axes is parallel to |this| WritingMode's
615
    // aLogicalAxis, and get its physical start side as well.
616
    LogicalAxis otherWMAxis = aOther.IsOrthogonalTo(*this) ?
617
      GetOrthogonalAxis(aLogicalAxis) : aLogicalAxis;
618
    mozilla::Side otherWMStartSide =
619
      aOther.PhysicalSide(MakeLogicalSide(otherWMAxis,
620
                                          eLogicalEdgeStart));
621
622
    NS_ASSERTION(myStartSide % 2 == otherWMStartSide % 2,
623
                 "Should end up with sides in the same physical axis");
624
    return myStartSide == otherWMStartSide;
625
  }
626
627
0
  uint8_t GetBits() const { return mWritingMode; }
628
629
0
  const char* DebugString() const {
630
0
    return IsVertical()
631
0
      ? IsVerticalLR()
632
0
        ? IsBidiLTR()
633
0
          ? IsSideways() ? "sw-lr-ltr" : "v-lr-ltr"
634
0
          : IsSideways() ? "sw-lr-rtl" : "v-lr-rtl"
635
0
        : IsBidiLTR()
636
0
          ? IsSideways() ? "sw-rl-ltr" : "v-rl-ltr"
637
0
          : IsSideways() ? "sw-rl-rtl" : "v-rl-rtl"
638
0
      : IsBidiLTR() ? "h-ltr" : "h-rtl"
639
0
      ;
640
0
  }
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
0
  {
659
0
    return WritingMode(eUnknownWritingMode);
660
0
  }
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
  { }
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
0
  { }
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
0
  { }
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
0
  {
765
0
    if (aWritingMode.IsVertical()) {
766
0
      I() = aWritingMode.IsInlineReversed() ? aContainerSize.height - aPoint.y
767
0
                                            : aPoint.y;
768
0
      B() = aWritingMode.IsVerticalLR() ? aPoint.x
769
0
                                        : aContainerSize.width - aPoint.x;
770
0
    } else {
771
0
      I() = aWritingMode.IsInlineReversed() ? aContainerSize.width - aPoint.x
772
0
                                            : aPoint.x;
773
0
      B() = aPoint.y;
774
0
    }
775
0
  }
776
777
  /**
778
   * Read-only (const) access to the logical coordinates.
779
   */
780
  nscoord I(WritingMode aWritingMode) const // inline-axis
781
0
  {
782
0
    CHECK_WRITING_MODE(aWritingMode);
783
0
    return mPoint.x;
784
0
  }
785
  nscoord B(WritingMode aWritingMode) const // block-axis
786
0
  {
787
0
    CHECK_WRITING_MODE(aWritingMode);
788
0
    return mPoint.y;
789
0
  }
790
  nscoord LineRelative(WritingMode aWritingMode,
791
                       const nsSize& aContainerSize) const // line-axis
792
  {
793
    CHECK_WRITING_MODE(aWritingMode);
794
    if (aWritingMode.IsBidiLTR()) {
795
      return I();
796
    }
797
    return (aWritingMode.IsVertical() ? aContainerSize.height
798
                                      : aContainerSize.width) - I();
799
  }
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
0
  {
807
0
    CHECK_WRITING_MODE(aWritingMode);
808
0
    return mPoint.x;
809
0
  }
810
  nscoord& B(WritingMode aWritingMode) // block-axis
811
0
  {
812
0
    CHECK_WRITING_MODE(aWritingMode);
813
0
    return mPoint.y;
814
0
  }
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
0
  {
823
0
    CHECK_WRITING_MODE(aWritingMode);
824
0
    if (aWritingMode.IsVertical()) {
825
0
      return nsPoint(aWritingMode.IsVerticalLR()
826
0
                     ? B() : aContainerSize.width - B(),
827
0
                     aWritingMode.IsInlineReversed()
828
0
                     ? aContainerSize.height - I() : I());
829
0
    } else {
830
0
      return nsPoint(aWritingMode.IsInlineReversed()
831
0
                     ? aContainerSize.width - I() : I(),
832
0
                     B());
833
0
    }
834
0
  }
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
0
  {
842
0
    CHECK_WRITING_MODE(aFromMode);
843
0
    return aToMode == aFromMode ?
844
0
      *this : LogicalPoint(aToMode,
845
0
                           GetPhysicalPoint(aFromMode, aContainerSize),
846
0
                           aContainerSize);
847
0
  }
848
849
  bool operator==(const LogicalPoint& aOther) const
850
  {
851
    CHECK_WRITING_MODE(aOther.GetWritingMode());
852
    return mPoint == aOther.mPoint;
853
  }
854
855
  bool operator!=(const LogicalPoint& aOther) const
856
0
  {
857
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
858
0
    return mPoint != aOther.mPoint;
859
0
  }
860
861
  LogicalPoint operator+(const LogicalPoint& aOther) const
862
0
  {
863
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
864
0
    // In non-debug builds, LogicalPoint does not store the WritingMode,
865
0
    // so the first parameter here (which will always be eUnknownWritingMode)
866
0
    // is ignored.
867
0
    return LogicalPoint(GetWritingMode(),
868
0
                        mPoint.x + aOther.mPoint.x,
869
0
                        mPoint.y + aOther.mPoint.y);
870
0
  }
871
872
  LogicalPoint& operator+=(const LogicalPoint& aOther)
873
  {
874
    CHECK_WRITING_MODE(aOther.GetWritingMode());
875
    I() += aOther.I();
876
    B() += aOther.B();
877
    return *this;
878
  }
879
880
  LogicalPoint operator-(const LogicalPoint& aOther) const
881
0
  {
882
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
883
0
    // In non-debug builds, LogicalPoint does not store the WritingMode,
884
0
    // so the first parameter here (which will always be eUnknownWritingMode)
885
0
    // is ignored.
886
0
    return LogicalPoint(GetWritingMode(),
887
0
                        mPoint.x - aOther.mPoint.x,
888
0
                        mPoint.y - aOther.mPoint.y);
889
0
  }
890
891
  LogicalPoint& operator-=(const LogicalPoint& aOther)
892
0
  {
893
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
894
0
    I() -= aOther.I();
895
0
    B() -= aOther.B();
896
0
    return *this;
897
0
  }
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
0
  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
0
  {
928
0
    return mPoint.x;
929
0
  }
930
  nscoord B() const // block-axis
931
0
  {
932
0
    return mPoint.y;
933
0
  }
934
935
  nscoord& I() // inline-axis
936
0
  {
937
0
    return mPoint.x;
938
0
  }
939
  nscoord& B() // block-axis
940
0
  {
941
0
    return mPoint.y;
942
0
  }
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
0
  { }
967
968
  LogicalSize(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
969
    :
970
#ifdef DEBUG
971
      mWritingMode(aWritingMode),
972
#endif
973
      mSize(aISize, aBSize)
974
0
  { }
975
976
  LogicalSize(WritingMode aWritingMode, const nsSize& aPhysicalSize)
977
#ifdef DEBUG
978
    : mWritingMode(aWritingMode)
979
#endif
980
0
  {
981
0
    if (aWritingMode.IsVertical()) {
982
0
      ISize() = aPhysicalSize.height;
983
0
      BSize() = aPhysicalSize.width;
984
0
    } else {
985
0
      ISize() = aPhysicalSize.width;
986
0
      BSize() = aPhysicalSize.height;
987
0
    }
988
0
  }
989
990
  void SizeTo(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
991
  {
992
    CHECK_WRITING_MODE(aWritingMode);
993
    mSize.SizeTo(aISize, aBSize);
994
  }
995
996
  /**
997
   * Dimensions in logical and physical terms
998
   */
999
  nscoord ISize(WritingMode aWritingMode) const // inline-size
1000
0
  {
1001
0
    CHECK_WRITING_MODE(aWritingMode);
1002
0
    return mSize.width;
1003
0
  }
1004
  nscoord BSize(WritingMode aWritingMode) const // block-size
1005
0
  {
1006
0
    CHECK_WRITING_MODE(aWritingMode);
1007
0
    return mSize.height;
1008
0
  }
1009
  nscoord Size(LogicalAxis aAxis, WritingMode aWM) const
1010
0
  {
1011
0
    return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
1012
0
  }
1013
1014
  nscoord Width(WritingMode aWritingMode) const
1015
  {
1016
    CHECK_WRITING_MODE(aWritingMode);
1017
    return aWritingMode.IsVertical() ? BSize() : ISize();
1018
  }
1019
  nscoord Height(WritingMode aWritingMode) const
1020
  {
1021
    CHECK_WRITING_MODE(aWritingMode);
1022
    return aWritingMode.IsVertical() ? ISize() : BSize();
1023
  }
1024
1025
  /**
1026
   * Writable references to the logical dimensions
1027
   */
1028
  nscoord& ISize(WritingMode aWritingMode) // inline-size
1029
0
  {
1030
0
    CHECK_WRITING_MODE(aWritingMode);
1031
0
    return mSize.width;
1032
0
  }
1033
  nscoord& BSize(WritingMode aWritingMode) // block-size
1034
0
  {
1035
0
    CHECK_WRITING_MODE(aWritingMode);
1036
0
    return mSize.height;
1037
0
  }
1038
  nscoord& Size(LogicalAxis aAxis, WritingMode aWM)
1039
0
  {
1040
0
    return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
1041
0
  }
1042
1043
  /**
1044
   * Return an nsSize containing our physical dimensions
1045
   */
1046
  nsSize GetPhysicalSize(WritingMode aWritingMode) const
1047
0
  {
1048
0
    CHECK_WRITING_MODE(aWritingMode);
1049
0
    return aWritingMode.IsVertical() ?
1050
0
      nsSize(BSize(), ISize()) : nsSize(ISize(), BSize());
1051
0
  }
1052
1053
  /**
1054
   * Return a LogicalSize representing this size in a different writing mode
1055
   */
1056
  LogicalSize ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
1057
0
  {
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
0
    // the writing mode
1067
0
    return (aToMode == aFromMode || !aToMode.IsOrthogonalTo(aFromMode))
1068
0
           ? *this : LogicalSize(aToMode, BSize(), ISize());
1069
0
#endif
1070
0
  }
1071
1072
  /**
1073
   * Test if a size is (0, 0).
1074
   */
1075
  bool IsAllZero() const
1076
0
  {
1077
0
    return ISize() == 0 && BSize() == 0;
1078
0
  }
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
0
  {
1086
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
1087
0
    return mSize == aOther.mSize;
1088
0
  }
1089
1090
  bool operator!=(const LogicalSize& aOther) const
1091
  {
1092
    CHECK_WRITING_MODE(aOther.GetWritingMode());
1093
    return mSize != aOther.mSize;
1094
  }
1095
1096
  LogicalSize operator+(const LogicalSize& aOther) const
1097
0
  {
1098
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
1099
0
    return LogicalSize(GetWritingMode(), ISize() + aOther.ISize(),
1100
0
                                         BSize() + aOther.BSize());
1101
0
  }
1102
  LogicalSize& operator+=(const LogicalSize& aOther)
1103
  {
1104
    CHECK_WRITING_MODE(aOther.GetWritingMode());
1105
    ISize() += aOther.ISize();
1106
    BSize() += aOther.BSize();
1107
    return *this;
1108
  }
1109
1110
  LogicalSize operator-(const LogicalSize& aOther) const
1111
0
  {
1112
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
1113
0
    return LogicalSize(GetWritingMode(), ISize() - aOther.ISize(),
1114
0
                                         BSize() - aOther.BSize());
1115
0
  }
1116
  LogicalSize& operator-=(const LogicalSize& aOther)
1117
0
  {
1118
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
1119
0
    ISize() -= aOther.ISize();
1120
0
    BSize() -= aOther.BSize();
1121
0
    return *this;
1122
0
  }
1123
1124
private:
1125
  friend class LogicalRect;
1126
1127
  LogicalSize() = delete;
1128
1129
#ifdef DEBUG
1130
  WritingMode GetWritingMode() const { return mWritingMode; }
1131
#else
1132
0
  WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1133
#endif
1134
1135
  nscoord ISize() const // inline-size
1136
0
  {
1137
0
    return mSize.width;
1138
0
  }
1139
  nscoord BSize() const // block-size
1140
0
  {
1141
0
    return mSize.height;
1142
0
  }
1143
1144
  nscoord& ISize() // inline-size
1145
0
  {
1146
0
    return mSize.width;
1147
0
  }
1148
  nscoord& BSize() // block-size
1149
0
  {
1150
0
    return mSize.height;
1151
0
  }
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
0
  { }
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
0
  { }
1181
1182
  LogicalMargin(WritingMode aWritingMode, const nsMargin& aPhysicalMargin)
1183
#ifdef DEBUG
1184
    : mWritingMode(aWritingMode)
1185
#endif
1186
0
  {
1187
0
    if (aWritingMode.IsVertical()) {
1188
0
      if (aWritingMode.IsVerticalLR()) {
1189
0
        mMargin.top = aPhysicalMargin.left;
1190
0
        mMargin.bottom = aPhysicalMargin.right;
1191
0
      } else {
1192
0
        mMargin.top = aPhysicalMargin.right;
1193
0
        mMargin.bottom = aPhysicalMargin.left;
1194
0
      }
1195
0
      if (aWritingMode.IsInlineReversed()) {
1196
0
        mMargin.left = aPhysicalMargin.bottom;
1197
0
        mMargin.right = aPhysicalMargin.top;
1198
0
      } else {
1199
0
        mMargin.left = aPhysicalMargin.top;
1200
0
        mMargin.right = aPhysicalMargin.bottom;
1201
0
      }
1202
0
    } else {
1203
0
      mMargin.top = aPhysicalMargin.top;
1204
0
      mMargin.bottom = aPhysicalMargin.bottom;
1205
0
      if (aWritingMode.IsInlineReversed()) {
1206
0
        mMargin.left = aPhysicalMargin.right;
1207
0
        mMargin.right = aPhysicalMargin.left;
1208
0
      } else {
1209
0
        mMargin.left = aPhysicalMargin.left;
1210
0
        mMargin.right = aPhysicalMargin.right;
1211
0
      }
1212
0
    }
1213
0
  }
1214
1215
  nscoord IStart(WritingMode aWritingMode) const // inline-start margin
1216
0
  {
1217
0
    CHECK_WRITING_MODE(aWritingMode);
1218
0
    return mMargin.left;
1219
0
  }
1220
  nscoord IEnd(WritingMode aWritingMode) const // inline-end margin
1221
0
  {
1222
0
    CHECK_WRITING_MODE(aWritingMode);
1223
0
    return mMargin.right;
1224
0
  }
1225
  nscoord BStart(WritingMode aWritingMode) const // block-start margin
1226
0
  {
1227
0
    CHECK_WRITING_MODE(aWritingMode);
1228
0
    return mMargin.top;
1229
0
  }
1230
  nscoord BEnd(WritingMode aWritingMode) const // block-end margin
1231
0
  {
1232
0
    CHECK_WRITING_MODE(aWritingMode);
1233
0
    return mMargin.bottom;
1234
0
  }
1235
  nscoord Start(LogicalAxis aAxis, WritingMode aWM) const
1236
0
  {
1237
0
    return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1238
0
  }
1239
  nscoord End(LogicalAxis aAxis, WritingMode aWM) const
1240
0
  {
1241
0
    return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1242
0
  }
1243
1244
  nscoord& IStart(WritingMode aWritingMode) // inline-start margin
1245
0
  {
1246
0
    CHECK_WRITING_MODE(aWritingMode);
1247
0
    return mMargin.left;
1248
0
  }
1249
  nscoord& IEnd(WritingMode aWritingMode) // inline-end margin
1250
0
  {
1251
0
    CHECK_WRITING_MODE(aWritingMode);
1252
0
    return mMargin.right;
1253
0
  }
1254
  nscoord& BStart(WritingMode aWritingMode) // block-start margin
1255
0
  {
1256
0
    CHECK_WRITING_MODE(aWritingMode);
1257
0
    return mMargin.top;
1258
0
  }
1259
  nscoord& BEnd(WritingMode aWritingMode) // block-end margin
1260
0
  {
1261
0
    CHECK_WRITING_MODE(aWritingMode);
1262
0
    return mMargin.bottom;
1263
0
  }
1264
  nscoord& Start(LogicalAxis aAxis, WritingMode aWM)
1265
0
  {
1266
0
    return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1267
0
  }
1268
  nscoord& End(LogicalAxis aAxis, WritingMode aWM)
1269
0
  {
1270
0
    return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1271
0
  }
1272
1273
  nscoord IStartEnd(WritingMode aWritingMode) const // inline margins
1274
0
  {
1275
0
    CHECK_WRITING_MODE(aWritingMode);
1276
0
    return mMargin.LeftRight();
1277
0
  }
1278
  nscoord BStartEnd(WritingMode aWritingMode) const // block margins
1279
0
  {
1280
0
    CHECK_WRITING_MODE(aWritingMode);
1281
0
    return mMargin.TopBottom();
1282
0
  }
1283
  nscoord StartEnd(LogicalAxis aAxis, WritingMode aWM) const
1284
0
  {
1285
0
    return aAxis == eLogicalAxisInline ? IStartEnd(aWM) : BStartEnd(aWM);
1286
0
  }
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
0
  {
1300
0
    // We don't need to CHECK_WRITING_MODE here because the IStart or IEnd
1301
0
    // accessor that we call will do it.
1302
0
    return aWritingMode.IsBidiLTR()
1303
0
           ? IStart(aWritingMode) : IEnd(aWritingMode);
1304
0
  }
1305
  nscoord LineRight(WritingMode aWritingMode) const
1306
0
  {
1307
0
    return aWritingMode.IsBidiLTR()
1308
0
           ? IEnd(aWritingMode) : IStart(aWritingMode);
1309
0
  }
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
0
  {
1317
0
    CHECK_WRITING_MODE(aWritingMode);
1318
0
    return LogicalSize(aWritingMode, IStartEnd(), BStartEnd());
1319
0
  }
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
  {
1327
    CHECK_WRITING_MODE(aWritingMode);
1328
    return aWritingMode.IsVertical() ?
1329
      (aWritingMode.IsInlineReversed() ? IEnd() : IStart()) : BStart();
1330
  }
1331
1332
  nscoord Bottom(WritingMode aWritingMode) const
1333
0
  {
1334
0
    CHECK_WRITING_MODE(aWritingMode);
1335
0
    return aWritingMode.IsVertical() ?
1336
0
      (aWritingMode.IsInlineReversed() ? IStart() : IEnd()) : BEnd();
1337
0
  }
1338
1339
  nscoord Left(WritingMode aWritingMode) const
1340
  {
1341
    CHECK_WRITING_MODE(aWritingMode);
1342
    return aWritingMode.IsVertical() ?
1343
      (aWritingMode.IsVerticalLR() ? BStart() : BEnd()) :
1344
      (aWritingMode.IsInlineReversed() ? IEnd() : IStart());
1345
  }
1346
1347
  nscoord Right(WritingMode aWritingMode) const
1348
0
  {
1349
0
    CHECK_WRITING_MODE(aWritingMode);
1350
0
    return aWritingMode.IsVertical() ?
1351
0
      (aWritingMode.IsVerticalLR() ? BEnd() : BStart()) :
1352
0
      (aWritingMode.IsInlineReversed() ? IStart() : IEnd());
1353
0
  }
1354
1355
  nscoord LeftRight(WritingMode aWritingMode) const
1356
0
  {
1357
0
    CHECK_WRITING_MODE(aWritingMode);
1358
0
    return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
1359
0
  }
1360
1361
  nscoord TopBottom(WritingMode aWritingMode) const
1362
0
  {
1363
0
    CHECK_WRITING_MODE(aWritingMode);
1364
0
    return aWritingMode.IsVertical() ? IStartEnd() : BStartEnd();
1365
0
  }
1366
1367
  void SizeTo(WritingMode aWritingMode,
1368
              nscoord aBStart, nscoord aIEnd, nscoord aBEnd, nscoord aIStart)
1369
  {
1370
    CHECK_WRITING_MODE(aWritingMode);
1371
    mMargin.SizeTo(aBStart, aIEnd, aBEnd, aIStart);
1372
  }
1373
1374
  /**
1375
   * Return an nsMargin containing our physical coordinates
1376
   */
1377
  nsMargin GetPhysicalMargin(WritingMode aWritingMode) const
1378
0
  {
1379
0
    CHECK_WRITING_MODE(aWritingMode);
1380
0
    return aWritingMode.IsVertical()
1381
0
           ? (aWritingMode.IsVerticalLR()
1382
0
             ? (aWritingMode.IsInlineReversed()
1383
0
               ? nsMargin(IEnd(), BEnd(), IStart(), BStart())
1384
0
               : nsMargin(IStart(), BEnd(), IEnd(), BStart()))
1385
0
             : (aWritingMode.IsInlineReversed()
1386
0
               ? nsMargin(IEnd(), BStart(), IStart(), BEnd())
1387
0
               : nsMargin(IStart(), BStart(), IEnd(), BEnd())))
1388
0
           : (aWritingMode.IsInlineReversed()
1389
0
             ? nsMargin(BStart(), IStart(), BEnd(), IEnd())
1390
0
             : nsMargin(BStart(), IEnd(), BEnd(), IStart()));
1391
0
  }
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
0
  {
1399
0
    CHECK_WRITING_MODE(aFromMode);
1400
0
    return aToMode == aFromMode ?
1401
0
      *this : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
1402
0
  }
1403
1404
  void ApplySkipSides(LogicalSides aSkipSides)
1405
0
  {
1406
0
    if (aSkipSides.BStart()) {
1407
0
      BStart() = 0;
1408
0
    }
1409
0
    if (aSkipSides.BEnd()) {
1410
0
      BEnd() = 0;
1411
0
    }
1412
0
    if (aSkipSides.IStart()) {
1413
0
      IStart() = 0;
1414
0
    }
1415
0
    if (aSkipSides.IEnd()) {
1416
0
      IEnd() = 0;
1417
0
    }
1418
0
  }
1419
1420
  bool IsAllZero() const
1421
0
  {
1422
0
    return (mMargin.left == 0 && mMargin.top == 0 &&
1423
0
            mMargin.right == 0 && mMargin.bottom == 0);
1424
0
  }
1425
1426
0
  LogicalMargin operator+(const LogicalMargin& aMargin) const {
1427
0
    CHECK_WRITING_MODE(aMargin.GetWritingMode());
1428
0
    return LogicalMargin(GetWritingMode(),
1429
0
                         BStart() + aMargin.BStart(),
1430
0
                         IEnd() + aMargin.IEnd(),
1431
0
                         BEnd() + aMargin.BEnd(),
1432
0
                         IStart() + aMargin.IStart());
1433
0
  }
1434
1435
  LogicalMargin operator+=(const LogicalMargin& aMargin)
1436
0
  {
1437
0
    CHECK_WRITING_MODE(aMargin.GetWritingMode());
1438
0
    mMargin += aMargin.mMargin;
1439
0
    return *this;
1440
0
  }
1441
1442
0
  LogicalMargin operator-(const LogicalMargin& aMargin) const {
1443
0
    CHECK_WRITING_MODE(aMargin.GetWritingMode());
1444
0
    return LogicalMargin(GetWritingMode(),
1445
0
                         BStart() - aMargin.BStart(),
1446
0
                         IEnd() - aMargin.IEnd(),
1447
0
                         BEnd() - aMargin.BEnd(),
1448
0
                         IStart() - aMargin.IStart());
1449
0
  }
1450
1451
private:
1452
  friend class LogicalRect;
1453
1454
  LogicalMargin() = delete;
1455
1456
#ifdef DEBUG
1457
  WritingMode GetWritingMode() const { return mWritingMode; }
1458
#else
1459
0
  WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1460
#endif
1461
1462
  nscoord IStart() const // inline-start margin
1463
0
  {
1464
0
    return mMargin.left;
1465
0
  }
1466
  nscoord IEnd() const // inline-end margin
1467
0
  {
1468
0
    return mMargin.right;
1469
0
  }
1470
  nscoord BStart() const // block-start margin
1471
0
  {
1472
0
    return mMargin.top;
1473
0
  }
1474
  nscoord BEnd() const // block-end margin
1475
0
  {
1476
0
    return mMargin.bottom;
1477
0
  }
1478
1479
  nscoord& IStart() // inline-start margin
1480
0
  {
1481
0
    return mMargin.left;
1482
0
  }
1483
  nscoord& IEnd() // inline-end margin
1484
0
  {
1485
0
    return mMargin.right;
1486
0
  }
1487
  nscoord& BStart() // block-start margin
1488
0
  {
1489
0
    return mMargin.top;
1490
0
  }
1491
  nscoord& BEnd() // block-end margin
1492
0
  {
1493
0
    return mMargin.bottom;
1494
0
  }
1495
1496
  nscoord IStartEnd() const // inline margins
1497
0
  {
1498
0
    return mMargin.LeftRight();
1499
0
  }
1500
  nscoord BStartEnd() const // block margins
1501
0
  {
1502
0
    return mMargin.TopBottom();
1503
0
  }
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
0
  { }
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
0
  { }
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
  {
1552
    CHECK_WRITING_MODE(aOrigin.GetWritingMode());
1553
    CHECK_WRITING_MODE(aSize.GetWritingMode());
1554
  }
1555
1556
  LogicalRect(WritingMode aWritingMode,
1557
              const nsRect& aRect,
1558
              const nsSize& aContainerSize)
1559
#ifdef DEBUG
1560
    : mWritingMode(aWritingMode)
1561
#endif
1562
0
  {
1563
0
    if (aWritingMode.IsVertical()) {
1564
0
      mBStart = aWritingMode.IsVerticalLR()
1565
0
               ? aRect.X() : aContainerSize.width - aRect.XMost();
1566
0
      mIStart = aWritingMode.IsInlineReversed()
1567
0
               ? aContainerSize.height - aRect.YMost() : aRect.Y();
1568
0
      mBSize = aRect.Width();
1569
0
      mISize = aRect.Height();
1570
0
    } else {
1571
0
      mIStart = aWritingMode.IsInlineReversed()
1572
0
               ? aContainerSize.width - aRect.XMost() : aRect.X();
1573
0
      mBStart = aRect.Y();
1574
0
      mISize = aRect.Width();
1575
0
      mBSize = aRect.Height();
1576
0
    }
1577
0
  }
1578
1579
  /**
1580
   * Inline- and block-dimension geometry.
1581
   */
1582
  nscoord IStart(WritingMode aWritingMode) const // inline-start edge
1583
0
  {
1584
0
    CHECK_WRITING_MODE(aWritingMode);
1585
0
    return mIStart;
1586
0
  }
1587
  nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
1588
0
  {
1589
0
    CHECK_WRITING_MODE(aWritingMode);
1590
0
    return mIStart + mISize;
1591
0
  }
1592
  nscoord ISize(WritingMode aWritingMode) const // inline-size
1593
0
  {
1594
0
    CHECK_WRITING_MODE(aWritingMode);
1595
0
    return mISize;
1596
0
  }
1597
1598
  nscoord BStart(WritingMode aWritingMode) const // block-start edge
1599
0
  {
1600
0
    CHECK_WRITING_MODE(aWritingMode);
1601
0
    return mBStart;
1602
0
  }
1603
  nscoord BEnd(WritingMode aWritingMode) const // block-end edge
1604
0
  {
1605
0
    CHECK_WRITING_MODE(aWritingMode);
1606
0
    return mBStart + mBSize;
1607
0
  }
1608
  nscoord BSize(WritingMode aWritingMode) const // block-size
1609
0
  {
1610
0
    CHECK_WRITING_MODE(aWritingMode);
1611
0
    return mBSize;
1612
0
  }
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
0
  {
1620
0
    CHECK_WRITING_MODE(aWritingMode);
1621
0
    return mIStart;
1622
0
  }
1623
  nscoord& ISize(WritingMode aWritingMode) // inline-size
1624
0
  {
1625
0
    CHECK_WRITING_MODE(aWritingMode);
1626
0
    return mISize;
1627
0
  }
1628
  nscoord& BStart(WritingMode aWritingMode) // block-start edge
1629
0
  {
1630
0
    CHECK_WRITING_MODE(aWritingMode);
1631
0
    return mBStart;
1632
0
  }
1633
  nscoord& BSize(WritingMode aWritingMode) // block-size
1634
0
  {
1635
0
    CHECK_WRITING_MODE(aWritingMode);
1636
0
    return mBSize;
1637
0
  }
1638
1639
  /**
1640
   * Accessors for line-relative coordinates
1641
   */
1642
  nscoord LineLeft(WritingMode aWritingMode,
1643
                   const nsSize& aContainerSize) const
1644
  {
1645
    CHECK_WRITING_MODE(aWritingMode);
1646
    if (aWritingMode.IsBidiLTR()) {
1647
      return IStart();
1648
    }
1649
    nscoord containerISize =
1650
      aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width;
1651
    return containerISize - IEnd();
1652
  }
1653
  nscoord LineRight(WritingMode aWritingMode,
1654
                    const nsSize& aContainerSize) const
1655
  {
1656
    CHECK_WRITING_MODE(aWritingMode);
1657
    if (aWritingMode.IsBidiLTR()) {
1658
      return IEnd();
1659
    }
1660
    nscoord containerISize =
1661
      aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width;
1662
    return containerISize - IStart();
1663
  }
1664
1665
  /**
1666
   * Physical coordinates of the rect.
1667
   */
1668
  nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
1669
0
  {
1670
0
    CHECK_WRITING_MODE(aWritingMode);
1671
0
    if (aWritingMode.IsVertical()) {
1672
0
      return aWritingMode.IsVerticalLR() ?
1673
0
             mBStart : aContainerWidth - BEnd();
1674
0
    } else {
1675
0
      return aWritingMode.IsInlineReversed() ?
1676
0
             aContainerWidth - IEnd() : mIStart;
1677
0
    }
1678
0
  }
1679
1680
  nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const
1681
0
  {
1682
0
    CHECK_WRITING_MODE(aWritingMode);
1683
0
    if (aWritingMode.IsVertical()) {
1684
0
      return aWritingMode.IsInlineReversed() ? aContainerHeight - IEnd()
1685
0
                                             : mIStart;
1686
0
    } else {
1687
0
      return mBStart;
1688
0
    }
1689
0
  }
1690
1691
  nscoord Width(WritingMode aWritingMode) const
1692
0
  {
1693
0
    CHECK_WRITING_MODE(aWritingMode);
1694
0
    return aWritingMode.IsVertical() ? mBSize : mISize;
1695
0
  }
1696
1697
  nscoord Height(WritingMode aWritingMode) const
1698
0
  {
1699
0
    CHECK_WRITING_MODE(aWritingMode);
1700
0
    return aWritingMode.IsVertical() ? mISize : mBSize;
1701
0
  }
1702
1703
  nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const
1704
0
  {
1705
0
    CHECK_WRITING_MODE(aWritingMode);
1706
0
    if (aWritingMode.IsVertical()) {
1707
0
      return aWritingMode.IsVerticalLR() ?
1708
0
             BEnd() : aContainerWidth - mBStart;
1709
0
    } else {
1710
0
      return aWritingMode.IsInlineReversed() ?
1711
0
             aContainerWidth - mIStart : IEnd();
1712
0
    }
1713
0
  }
1714
1715
  nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const
1716
0
  {
1717
0
    CHECK_WRITING_MODE(aWritingMode);
1718
0
    if (aWritingMode.IsVertical()) {
1719
0
      return aWritingMode.IsInlineReversed() ? aContainerHeight - mIStart
1720
0
                                             : IEnd();
1721
0
    } else {
1722
0
      return mBStart;
1723
0
    }
1724
0
  }
1725
1726
  bool IsEmpty() const
1727
0
  {
1728
0
    return mISize <= 0 || mBSize <= 0;
1729
0
  }
1730
1731
  bool IsAllZero() const
1732
  {
1733
    return (mIStart == 0 && mBStart == 0 &&
1734
            mISize == 0 && mBSize == 0);
1735
  }
1736
1737
  bool IsZeroSize() const
1738
0
  {
1739
0
    return (mISize == 0 && mBSize == 0);
1740
0
  }
1741
1742
0
  void SetEmpty() { mISize = mBSize = 0; }
1743
1744
  bool IsEqualEdges(const LogicalRect aOther) const
1745
0
  {
1746
0
    CHECK_WRITING_MODE(aOther.GetWritingMode());
1747
0
    bool result = mIStart == aOther.mIStart && mBStart == aOther.mBStart &&
1748
0
                  mISize == aOther.mISize && mBSize == aOther.mBSize;
1749
0
1750
0
    // We want the same result as nsRect, so assert we get it.
1751
0
    MOZ_ASSERT(result == nsRect(mIStart, mBStart, mISize, mBSize).
1752
0
                           IsEqualEdges(nsRect(aOther.mIStart, aOther.mBStart,
1753
0
                                               aOther.mISize, aOther.mBSize)));
1754
0
    return result;
1755
0
  }
1756
1757
  LogicalPoint Origin(WritingMode aWritingMode) const
1758
0
  {
1759
0
    CHECK_WRITING_MODE(aWritingMode);
1760
0
    return LogicalPoint(aWritingMode, IStart(), BStart());
1761
0
  }
1762
  void SetOrigin(WritingMode aWritingMode, const LogicalPoint& aPoint)
1763
0
  {
1764
0
    IStart(aWritingMode) = aPoint.I(aWritingMode);
1765
0
    BStart(aWritingMode) = aPoint.B(aWritingMode);
1766
0
  }
1767
1768
  LogicalSize Size(WritingMode aWritingMode) const
1769
  {
1770
    CHECK_WRITING_MODE(aWritingMode);
1771
    return LogicalSize(aWritingMode, ISize(), BSize());
1772
  }
1773
1774
  LogicalRect operator+(const LogicalPoint& aPoint) const
1775
0
  {
1776
0
    CHECK_WRITING_MODE(aPoint.GetWritingMode());
1777
0
    return LogicalRect(GetWritingMode(),
1778
0
                       IStart() + aPoint.I(), BStart() + aPoint.B(),
1779
0
                       ISize(), BSize());
1780
0
  }
1781
1782
  LogicalRect& operator+=(const LogicalPoint& aPoint)
1783
0
  {
1784
0
    CHECK_WRITING_MODE(aPoint.GetWritingMode());
1785
0
    mIStart += aPoint.mPoint.x;
1786
0
    mBStart += aPoint.mPoint.y;
1787
0
    return *this;
1788
0
  }
1789
1790
  LogicalRect operator-(const LogicalPoint& aPoint) const
1791
0
  {
1792
0
    CHECK_WRITING_MODE(aPoint.GetWritingMode());
1793
0
    return LogicalRect(GetWritingMode(),
1794
0
                       IStart() - aPoint.I(), BStart() - aPoint.B(),
1795
0
                       ISize(), BSize());
1796
0
  }
1797
1798
  LogicalRect& operator-=(const LogicalPoint& aPoint)
1799
0
  {
1800
0
    CHECK_WRITING_MODE(aPoint.GetWritingMode());
1801
0
    mIStart -= aPoint.mPoint.x;
1802
0
    mBStart -= aPoint.mPoint.y;
1803
0
    return *this;
1804
0
  }
1805
1806
  void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta)
1807
  {
1808
    CHECK_WRITING_MODE(aWritingMode);
1809
    CHECK_WRITING_MODE(aDelta.GetWritingMode());
1810
    IStart() += aDelta.I();
1811
    BStart() += aDelta.B();
1812
  }
1813
1814
  void Inflate(nscoord aD)
1815
0
  {
1816
0
#ifdef DEBUG
1817
0
    // Compute using nsRect and assert the results match
1818
0
    nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1819
0
    rectDebug.Inflate(aD);
1820
0
#endif
1821
0
    mIStart -= aD;
1822
0
    mBStart -= aD;
1823
0
    mISize += 2 * aD;
1824
0
    mBSize += 2 * aD;
1825
0
    MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1826
0
  }
1827
  void Inflate(nscoord aDI, nscoord aDB)
1828
0
  {
1829
0
#ifdef DEBUG
1830
0
    // Compute using nsRect and assert the results match
1831
0
    nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1832
0
    rectDebug.Inflate(aDI, aDB);
1833
0
#endif
1834
0
    mIStart -= aDI;
1835
0
    mBStart -= aDB;
1836
0
    mISize += 2 * aDI;
1837
0
    mBSize += 2 * aDB;
1838
0
    MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1839
0
  }
1840
  void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
1841
  {
1842
    CHECK_WRITING_MODE(aWritingMode);
1843
    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
    mBStart -= aMargin.mMargin.top;
1851
    mISize += aMargin.mMargin.LeftRight();
1852
    mBSize += aMargin.mMargin.TopBottom();
1853
    MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1854
  }
1855
1856
  void Deflate(nscoord aD)
1857
0
  {
1858
0
#ifdef DEBUG
1859
0
    // Compute using nsRect and assert the results match
1860
0
    nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1861
0
    rectDebug.Deflate(aD);
1862
0
#endif
1863
0
    mIStart += aD;
1864
0
    mBStart += aD;
1865
0
    mISize = std::max(0, mISize - 2 * aD);
1866
0
    mBSize = std::max(0, mBSize - 2 * aD);
1867
0
    MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1868
0
  }
1869
  void Deflate(nscoord aDI, nscoord aDB)
1870
0
  {
1871
0
#ifdef DEBUG
1872
0
    // Compute using nsRect and assert the results match
1873
0
    nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1874
0
    rectDebug.Deflate(aDI, aDB);
1875
0
#endif
1876
0
    mIStart += aDI;
1877
0
    mBStart += aDB;
1878
0
    mISize = std::max(0, mISize - 2 * aDI);
1879
0
    mBSize = std::max(0, mBSize - 2 * aDB);
1880
0
    MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1881
0
  }
1882
  void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
1883
  {
1884
    CHECK_WRITING_MODE(aWritingMode);
1885
    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
    mBStart += aMargin.mMargin.top;
1893
    mISize = std::max(0, mISize - aMargin.mMargin.LeftRight());
1894
    mBSize = std::max(0, mBSize - aMargin.mMargin.TopBottom());
1895
    MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1896
  }
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
0
  {
1905
0
    CHECK_WRITING_MODE(aWritingMode);
1906
0
    if (aWritingMode.IsVertical()) {
1907
0
      return nsRect(aWritingMode.IsVerticalLR()
1908
0
                    ? BStart() : aContainerSize.width - BEnd(),
1909
0
                    aWritingMode.IsInlineReversed()
1910
0
                    ?  aContainerSize.height - IEnd() : IStart(),
1911
0
                    BSize(), ISize());
1912
0
    } else {
1913
0
      return nsRect(aWritingMode.IsInlineReversed()
1914
0
                    ? aContainerSize.width - IEnd() : IStart(),
1915
0
                    BStart(), ISize(), BSize());
1916
0
    }
1917
0
  }
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
0
  {
1925
0
    CHECK_WRITING_MODE(aFromMode);
1926
0
    return aToMode == aFromMode ?
1927
0
      *this : LogicalRect(aToMode, GetPhysicalRect(aFromMode, aContainerSize),
1928
0
                          aContainerSize);
1929
0
  }
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
  {
1937
    CHECK_WRITING_MODE(aRect1.mWritingMode);
1938
    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
    nscoord iEnd = std::min(aRect1.IEnd(), aRect2.IEnd());
1949
    mIStart = std::max(aRect1.mIStart, aRect2.mIStart);
1950
    mISize = iEnd - mIStart;
1951
1952
    nscoord bEnd = std::min(aRect1.BEnd(), aRect2.BEnd());
1953
    mBStart = std::max(aRect1.mBStart, aRect2.mBStart);
1954
    mBSize = bEnd - mBStart;
1955
1956
    if (mISize < 0 || mBSize < 0) {
1957
      mISize = 0;
1958
      mBSize = 0;
1959
    }
1960
1961
    MOZ_ASSERT((rectDebug.IsEmpty() && (mISize == 0 || mBSize == 0)) || rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1962
    return mISize > 0 && mBSize > 0;
1963
  }
1964
1965
private:
1966
  LogicalRect() = delete;
1967
1968
#ifdef DEBUG
1969
  WritingMode GetWritingMode() const { return mWritingMode; }
1970
#else
1971
0
  WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1972
#endif
1973
1974
  nscoord IStart() const // inline-start edge
1975
0
  {
1976
0
    return mIStart;
1977
0
  }
1978
  nscoord IEnd() const // inline-end edge
1979
0
  {
1980
0
    return mIStart + mISize;
1981
0
  }
1982
  nscoord ISize() const // inline-size
1983
0
  {
1984
0
    return mISize;
1985
0
  }
1986
1987
  nscoord BStart() const // block-start edge
1988
0
  {
1989
0
    return mBStart;
1990
0
  }
1991
  nscoord BEnd() const // block-end edge
1992
0
  {
1993
0
    return mBStart + mBSize;
1994
0
  }
1995
  nscoord BSize() const // block-size
1996
0
  {
1997
0
    return mBSize;
1998
0
  }
1999
2000
  nscoord& IStart() // inline-start edge
2001
  {
2002
    return mIStart;
2003
  }
2004
  nscoord& ISize() // inline-size
2005
0
  {
2006
0
    return mISize;
2007
0
  }
2008
  nscoord& BStart() // block-start edge
2009
  {
2010
    return mBStart;
2011
  }
2012
  nscoord& BSize() // block-size
2013
0
  {
2014
0
    return mBSize;
2015
0
  }
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
0
{
2034
0
  return GetUnit(aWM.PhysicalSide(aSide));
2035
0
}
2036
2037
inline nsStyleUnit nsStyleSides::GetIStartUnit(mozilla::WritingMode aWM) const
2038
0
{
2039
0
  return GetUnit(aWM, mozilla::eLogicalSideIStart);
2040
0
}
2041
2042
inline nsStyleUnit nsStyleSides::GetBStartUnit(mozilla::WritingMode aWM) const
2043
0
{
2044
0
  return GetUnit(aWM, mozilla::eLogicalSideBStart);
2045
0
}
2046
2047
inline nsStyleUnit nsStyleSides::GetIEndUnit(mozilla::WritingMode aWM) const
2048
0
{
2049
0
  return GetUnit(aWM, mozilla::eLogicalSideIEnd);
2050
0
}
2051
2052
inline nsStyleUnit nsStyleSides::GetBEndUnit(mozilla::WritingMode aWM) const
2053
0
{
2054
0
  return GetUnit(aWM, mozilla::eLogicalSideBEnd);
2055
0
}
2056
2057
inline bool nsStyleSides::HasBlockAxisAuto(mozilla::WritingMode aWM) const
2058
{
2059
  return GetBStartUnit(aWM) == eStyleUnit_Auto ||
2060
         GetBEndUnit(aWM) == eStyleUnit_Auto;
2061
}
2062
2063
inline bool nsStyleSides::HasInlineAxisAuto(mozilla::WritingMode aWM) const
2064
{
2065
  return GetIStartUnit(aWM) == eStyleUnit_Auto ||
2066
         GetIEndUnit(aWM) == eStyleUnit_Auto;
2067
}
2068
2069
inline nsStyleCoord nsStyleSides::Get(mozilla::WritingMode aWM,
2070
                                      mozilla::LogicalSide aSide) const
2071
0
{
2072
0
  return Get(aWM.PhysicalSide(aSide));
2073
0
}
2074
2075
inline nsStyleCoord nsStyleSides::GetIStart(mozilla::WritingMode aWM) const
2076
0
{
2077
0
  return Get(aWM, mozilla::eLogicalSideIStart);
2078
0
}
2079
2080
inline nsStyleCoord nsStyleSides::GetBStart(mozilla::WritingMode aWM) const
2081
0
{
2082
0
  return Get(aWM, mozilla::eLogicalSideBStart);
2083
0
}
2084
2085
inline nsStyleCoord nsStyleSides::GetIEnd(mozilla::WritingMode aWM) const
2086
0
{
2087
0
  return Get(aWM, mozilla::eLogicalSideIEnd);
2088
0
}
2089
2090
inline nsStyleCoord nsStyleSides::GetBEnd(mozilla::WritingMode aWM) const
2091
0
{
2092
0
  return Get(aWM, mozilla::eLogicalSideBEnd);
2093
0
}
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
0
{
2099
0
  return aWM.IsVertical() ? mHeight : mWidth;
2100
0
}
2101
inline nsStyleCoord& nsStylePosition::MinISize(mozilla::WritingMode aWM)
2102
0
{
2103
0
  return aWM.IsVertical() ? mMinHeight : mMinWidth;
2104
0
}
2105
inline nsStyleCoord& nsStylePosition::MaxISize(mozilla::WritingMode aWM)
2106
0
{
2107
0
  return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
2108
0
}
2109
inline nsStyleCoord& nsStylePosition::BSize(mozilla::WritingMode aWM)
2110
0
{
2111
0
  return aWM.IsVertical() ? mWidth : mHeight;
2112
0
}
2113
inline nsStyleCoord& nsStylePosition::MinBSize(mozilla::WritingMode aWM)
2114
0
{
2115
0
  return aWM.IsVertical() ? mMinWidth : mMinHeight;
2116
0
}
2117
inline nsStyleCoord& nsStylePosition::MaxBSize(mozilla::WritingMode aWM)
2118
0
{
2119
0
  return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
2120
0
}
2121
2122
inline const nsStyleCoord&
2123
nsStylePosition::ISize(mozilla::WritingMode aWM) const
2124
0
{
2125
0
  return aWM.IsVertical() ? mHeight : mWidth;
2126
0
}
2127
inline const nsStyleCoord&
2128
nsStylePosition::MinISize(mozilla::WritingMode aWM) const
2129
0
{
2130
0
  return aWM.IsVertical() ? mMinHeight : mMinWidth;
2131
0
}
2132
inline const nsStyleCoord&
2133
nsStylePosition::MaxISize(mozilla::WritingMode aWM) const
2134
0
{
2135
0
  return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
2136
0
}
2137
inline const nsStyleCoord&
2138
nsStylePosition::BSize(mozilla::WritingMode aWM) const
2139
0
{
2140
0
  return aWM.IsVertical() ? mWidth : mHeight;
2141
0
}
2142
inline const nsStyleCoord&
2143
nsStylePosition::MinBSize(mozilla::WritingMode aWM) const
2144
0
{
2145
0
  return aWM.IsVertical() ? mMinWidth : mMinHeight;
2146
0
}
2147
inline const nsStyleCoord&
2148
nsStylePosition::MaxBSize(mozilla::WritingMode aWM) const
2149
0
{
2150
0
  return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
2151
0
}
2152
2153
inline bool
2154
nsStylePosition::ISizeDependsOnContainer(mozilla::WritingMode aWM) const
2155
{
2156
  return aWM.IsVertical() ? HeightDependsOnContainer()
2157
                          : WidthDependsOnContainer();
2158
}
2159
inline bool
2160
nsStylePosition::MinISizeDependsOnContainer(mozilla::WritingMode aWM) const
2161
{
2162
  return aWM.IsVertical() ? MinHeightDependsOnContainer()
2163
                          : MinWidthDependsOnContainer();
2164
}
2165
inline bool
2166
nsStylePosition::MaxISizeDependsOnContainer(mozilla::WritingMode aWM) const
2167
{
2168
  return aWM.IsVertical() ? MaxHeightDependsOnContainer()
2169
                          : MaxWidthDependsOnContainer();
2170
}
2171
inline bool
2172
nsStylePosition::BSizeDependsOnContainer(mozilla::WritingMode aWM) const
2173
0
{
2174
0
  return aWM.IsVertical() ? WidthDependsOnContainer()
2175
0
                          : HeightDependsOnContainer();
2176
0
}
2177
inline bool
2178
nsStylePosition::MinBSizeDependsOnContainer(mozilla::WritingMode aWM) const
2179
0
{
2180
0
  return aWM.IsVertical() ? MinWidthDependsOnContainer()
2181
0
                          : MinHeightDependsOnContainer();
2182
0
}
2183
inline bool
2184
nsStylePosition::MaxBSizeDependsOnContainer(mozilla::WritingMode aWM) const
2185
0
{
2186
0
  return aWM.IsVertical() ? MaxWidthDependsOnContainer()
2187
0
                          : MaxHeightDependsOnContainer();
2188
0
}
2189
2190
inline bool
2191
nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const
2192
{
2193
  return mMargin.HasBlockAxisAuto(aWM);
2194
}
2195
inline bool
2196
nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const
2197
{
2198
  return mMargin.HasInlineAxisAuto(aWM);
2199
}
2200
2201
#endif // WritingModes_h_