Bug 1858509 add thread-safety annotations around MediaSourceDemuxer::mMonitor r=alwu
[gecko.git] / layout / generic / WritingModes.h
blob2f28b737755e101ec43c7f12a2ae60100c300a2d
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/. */
7 #ifndef WritingModes_h_
8 #define WritingModes_h_
10 #include <ostream>
12 #include "mozilla/intl/BidiEmbeddingLevel.h"
13 #include "mozilla/ComputedStyle.h"
14 #include "mozilla/EnumeratedRange.h"
16 #include "nsRect.h"
17 #include "nsBidiUtils.h"
18 #include "nsStyleStruct.h"
20 // It is the caller's responsibility to operate on logical-coordinate objects
21 // with matched writing modes. Failure to do so will be a runtime bug; the
22 // compiler can't catch it, but in debug mode, we'll throw an assertion.
23 // NOTE that in non-debug builds, a writing mode mismatch error will NOT be
24 // detected, yet the results will be nonsense (and may lead to further layout
25 // failures). Therefore, it is important to test (and fuzz-test) writing-mode
26 // support using debug builds.
28 // Methods in logical-coordinate classes that take another logical-coordinate
29 // object as a parameter should call CHECK_WRITING_MODE on it to verify that
30 // the writing modes match.
31 // (In some cases, there are internal (private) methods that don't do this;
32 // such methods should only be used by other methods that have already checked
33 // the writing modes.)
34 // The check ignores the StyleWritingMode::VERTICAL_SIDEWAYS and
35 // StyleWritingMode::TEXT_SIDEWAYS bit of writing mode, because
36 // this does not affect the interpretation of logical coordinates.
38 #define CHECK_WRITING_MODE(param) \
39 NS_ASSERTION(param.IgnoreSideways() == GetWritingMode().IgnoreSideways(), \
40 "writing-mode mismatch")
42 namespace mozilla {
44 namespace widget {
45 struct IMENotification;
46 } // namespace widget
48 // Logical axis, edge, side and corner constants for use in various places.
49 enum LogicalAxis : uint8_t {
50 eLogicalAxisBlock = 0x0,
51 eLogicalAxisInline = 0x1
53 enum LogicalEdge { eLogicalEdgeStart = 0x0, eLogicalEdgeEnd = 0x1 };
54 enum LogicalSide : uint8_t {
55 eLogicalSideBStart = (eLogicalAxisBlock << 1) | eLogicalEdgeStart, // 0x0
56 eLogicalSideBEnd = (eLogicalAxisBlock << 1) | eLogicalEdgeEnd, // 0x1
57 eLogicalSideIStart = (eLogicalAxisInline << 1) | eLogicalEdgeStart, // 0x2
58 eLogicalSideIEnd = (eLogicalAxisInline << 1) | eLogicalEdgeEnd // 0x3
60 constexpr auto AllLogicalSides() {
61 return mozilla::MakeInclusiveEnumeratedRange(eLogicalSideBStart,
62 eLogicalSideIEnd);
65 enum LogicalCorner {
66 eLogicalCornerBStartIStart = 0,
67 eLogicalCornerBStartIEnd = 1,
68 eLogicalCornerBEndIEnd = 2,
69 eLogicalCornerBEndIStart = 3
72 // Physical axis constants.
73 enum PhysicalAxis { eAxisVertical = 0x0, eAxisHorizontal = 0x1 };
75 // Represents zero or more physical axes.
76 enum class PhysicalAxes : uint8_t {
77 None = 0x0,
78 Horizontal = 0x1,
79 Vertical = 0x2,
80 Both = Horizontal | Vertical,
82 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(PhysicalAxes)
84 inline LogicalAxis GetOrthogonalAxis(LogicalAxis aAxis) {
85 return aAxis == eLogicalAxisBlock ? eLogicalAxisInline : eLogicalAxisBlock;
88 inline bool IsInline(LogicalSide aSide) { return aSide & 0x2; }
89 inline bool IsBlock(LogicalSide aSide) { return !IsInline(aSide); }
90 inline bool IsEnd(LogicalSide aSide) { return aSide & 0x1; }
91 inline bool IsStart(LogicalSide aSide) { return !IsEnd(aSide); }
93 inline LogicalAxis GetAxis(LogicalSide aSide) {
94 return IsInline(aSide) ? eLogicalAxisInline : eLogicalAxisBlock;
97 inline LogicalEdge GetEdge(LogicalSide aSide) {
98 return IsEnd(aSide) ? eLogicalEdgeEnd : eLogicalEdgeStart;
101 inline LogicalEdge GetOppositeEdge(LogicalEdge aEdge) {
102 // This relies on the only two LogicalEdge enum values being 0 and 1.
103 return LogicalEdge(1 - aEdge);
106 inline LogicalSide MakeLogicalSide(LogicalAxis aAxis, LogicalEdge aEdge) {
107 return LogicalSide((aAxis << 1) | aEdge);
110 inline LogicalSide GetOppositeSide(LogicalSide aSide) {
111 return MakeLogicalSide(GetAxis(aSide), GetOppositeEdge(GetEdge(aSide)));
114 enum LogicalSideBits {
115 eLogicalSideBitsNone = 0,
116 eLogicalSideBitsBStart = 1 << eLogicalSideBStart,
117 eLogicalSideBitsBEnd = 1 << eLogicalSideBEnd,
118 eLogicalSideBitsIEnd = 1 << eLogicalSideIEnd,
119 eLogicalSideBitsIStart = 1 << eLogicalSideIStart,
120 eLogicalSideBitsBBoth = eLogicalSideBitsBStart | eLogicalSideBitsBEnd,
121 eLogicalSideBitsIBoth = eLogicalSideBitsIStart | eLogicalSideBitsIEnd,
122 eLogicalSideBitsAll = eLogicalSideBitsBBoth | eLogicalSideBitsIBoth
125 enum LineRelativeDir {
126 eLineRelativeDirOver = eLogicalSideBStart,
127 eLineRelativeDirUnder = eLogicalSideBEnd,
128 eLineRelativeDirLeft = eLogicalSideIStart,
129 eLineRelativeDirRight = eLogicalSideIEnd
133 * mozilla::WritingMode is an immutable class representing a
134 * writing mode.
136 * It efficiently stores the writing mode and can rapidly compute
137 * interesting things about it for use in layout.
139 * Writing modes are computed from the CSS 'direction',
140 * 'writing-mode', and 'text-orientation' properties.
141 * See CSS3 Writing Modes for more information
142 * http://www.w3.org/TR/css3-writing-modes/
144 class WritingMode {
145 public:
147 * Absolute inline flow direction
149 enum InlineDir {
150 eInlineLTR = 0x00, // text flows horizontally left to right
151 eInlineRTL = 0x02, // text flows horizontally right to left
152 eInlineTTB = 0x01, // text flows vertically top to bottom
153 eInlineBTT = 0x03, // text flows vertically bottom to top
157 * Absolute block flow direction
159 enum BlockDir {
160 eBlockTB = 0x00, // horizontal lines stack top to bottom
161 eBlockRL = 0x01, // vertical lines stack right to left
162 eBlockLR = 0x05, // vertical lines stack left to right
166 * Line-relative (bidi-relative) inline flow direction
168 enum BidiDir {
169 eBidiLTR = 0x00, // inline flow matches bidi LTR text
170 eBidiRTL = 0x10, // inline flow matches bidi RTL text
174 * Unknown writing mode (should never actually be stored or used anywhere).
176 enum { eUnknownWritingMode = 0xff };
179 * Return the absolute inline flow direction as an InlineDir
181 InlineDir GetInlineDir() const {
182 return InlineDir(mWritingMode._0 & eInlineMask);
186 * Return the absolute block flow direction as a BlockDir
188 BlockDir GetBlockDir() const {
189 return BlockDir(mWritingMode._0 & eBlockMask);
193 * Return the line-relative inline flow direction as a BidiDir
195 BidiDir GetBidiDir() const {
196 return BidiDir((mWritingMode & StyleWritingMode::RTL)._0);
200 * Return true if the inline flow direction is against physical direction
201 * (i.e. right-to-left or bottom-to-top).
202 * This occurs when writing-mode is sideways-lr OR direction is rtl (but not
203 * if both of those are true).
205 bool IsInlineReversed() const {
206 return !!(mWritingMode & StyleWritingMode::INLINE_REVERSED);
210 * Return true if bidi direction is LTR. (Convenience method)
212 bool IsBidiLTR() const { return eBidiLTR == GetBidiDir(); }
215 * Return true if bidi direction is RTL. (Convenience method)
217 bool IsBidiRTL() const { return eBidiRTL == GetBidiDir(); }
220 * True if it is vertical and vertical-lr, or is horizontal and bidi LTR.
222 bool IsPhysicalLTR() const {
223 return IsVertical() ? IsVerticalLR() : IsBidiLTR();
227 * True if it is vertical and vertical-rl, or is horizontal and bidi RTL.
229 bool IsPhysicalRTL() const {
230 return IsVertical() ? IsVerticalRL() : IsBidiRTL();
234 * True if vertical-mode block direction is LR (convenience method).
236 bool IsVerticalLR() const { return eBlockLR == GetBlockDir(); }
239 * True if vertical-mode block direction is RL (convenience method).
241 bool IsVerticalRL() const { return eBlockRL == GetBlockDir(); }
244 * True if vertical writing mode, i.e. when
245 * writing-mode: vertical-lr | vertical-rl.
247 bool IsVertical() const {
248 return !!(mWritingMode & StyleWritingMode::VERTICAL);
252 * True if line-over/line-under are inverted from block-start/block-end.
253 * This is true only when writing-mode is vertical-lr.
255 bool IsLineInverted() const {
256 return !!(mWritingMode & StyleWritingMode::LINE_INVERTED);
260 * Block-axis flow-relative to line-relative factor.
261 * May be used as a multiplication factor for block-axis coordinates
262 * to convert between flow- and line-relative coordinate systems (e.g.
263 * positioning an over- or under-line decoration).
265 int FlowRelativeToLineRelativeFactor() const {
266 return IsLineInverted() ? -1 : 1;
270 * True if vertical sideways writing mode, i.e. when
271 * writing-mode: sideways-lr | sideways-rl.
273 bool IsVerticalSideways() const {
274 return !!(mWritingMode & StyleWritingMode::VERTICAL_SIDEWAYS);
278 * True if this is writing-mode: sideways-rl (convenience method).
280 bool IsSidewaysRL() const { return IsVerticalRL() && IsVerticalSideways(); }
283 * True if this is writing-mode: sideways-lr (convenience method).
285 bool IsSidewaysLR() const { return IsVerticalLR() && IsVerticalSideways(); }
288 * True if either text-orientation or writing-mode will force all text to be
289 * rendered sideways in vertical lines, in which case we should prefer an
290 * alphabetic baseline; otherwise, the default is centered.
292 * Note that some glyph runs may be rendered sideways even if this is false,
293 * due to text-orientation:mixed resolution, but in that case the dominant
294 * baseline remains centered.
296 bool IsSideways() const {
297 return !!(mWritingMode & (StyleWritingMode::VERTICAL_SIDEWAYS |
298 StyleWritingMode::TEXT_SIDEWAYS));
301 #ifdef DEBUG
302 // Used by CHECK_WRITING_MODE to compare modes without regard for the
303 // StyleWritingMode::VERTICAL_SIDEWAYS or StyleWritingMode::TEXT_SIDEWAYS
304 // flags.
305 WritingMode IgnoreSideways() const {
306 return WritingMode(mWritingMode._0 & ~(StyleWritingMode::VERTICAL_SIDEWAYS |
307 StyleWritingMode::TEXT_SIDEWAYS)
308 ._0);
310 #endif
313 * Return true if boxes with this writing mode should use central baselines.
315 bool IsCentralBaseline() const { return IsVertical() && !IsSideways(); }
318 * Return true if boxes with this writing mode should use alphabetical
319 * baselines.
321 bool IsAlphabeticalBaseline() const { return !IsCentralBaseline(); }
323 static mozilla::PhysicalAxis PhysicalAxisForLogicalAxis(
324 uint8_t aWritingModeValue, LogicalAxis aAxis) {
325 // This relies on bit 0 of a writing-value mode indicating vertical
326 // orientation and bit 0 of a LogicalAxis value indicating the inline axis,
327 // so that it can correctly form mozilla::PhysicalAxis values using bit
328 // manipulation.
329 static_assert(uint8_t(StyleWritingModeProperty::HorizontalTb) == 0 &&
330 uint8_t(StyleWritingModeProperty::VerticalRl) == 1 &&
331 uint8_t(StyleWritingModeProperty::VerticalLr) == 3 &&
332 eLogicalAxisBlock == 0 && eLogicalAxisInline == 1 &&
333 eAxisVertical == 0 && eAxisHorizontal == 1,
334 "unexpected writing-mode, logical axis or physical axis "
335 "constant values");
336 return mozilla::PhysicalAxis((aWritingModeValue ^ aAxis) & 0x1);
339 mozilla::PhysicalAxis PhysicalAxis(LogicalAxis aAxis) const {
340 // This will set wm to either StyleWritingModel::HorizontalTB or
341 // StyleWritingModeProperty::VerticalRL, and not the other two (real
342 // and hypothetical) values. But this is fine; we only need to
343 // distinguish between vertical and horizontal in
344 // PhysicalAxisForLogicalAxis.
345 const auto wm = (mWritingMode & StyleWritingMode::VERTICAL)._0;
346 return PhysicalAxisForLogicalAxis(wm, aAxis);
349 static mozilla::Side PhysicalSideForBlockAxis(uint8_t aWritingModeValue,
350 LogicalEdge aEdge) {
351 // indexes are StyleWritingModeProperty values, which are the same as these
352 // two-bit values:
353 // bit 0 = the StyleWritingMode::VERTICAL value
354 // bit 1 = the StyleWritingMode::VERTICAL_LR value
355 static const mozilla::Side kLogicalBlockSides[][2] = {
356 {eSideTop, eSideBottom}, // horizontal-tb
357 {eSideRight, eSideLeft}, // vertical-rl
358 {eSideBottom, eSideTop}, // (horizontal-bt)
359 {eSideLeft, eSideRight}, // vertical-lr
362 // Ignore the SidewaysMask bit of the writing-mode value, as this has no
363 // effect on the side mappings.
364 aWritingModeValue &= ~kWritingModeSidewaysMask;
366 // What's left of the writing-mode should be in the range 0-3:
367 NS_ASSERTION(aWritingModeValue < 4, "invalid aWritingModeValue value");
369 return kLogicalBlockSides[aWritingModeValue][aEdge];
372 mozilla::Side PhysicalSideForInlineAxis(LogicalEdge aEdge) const {
373 // indexes are four-bit values:
374 // bit 0 = the StyleWritingMode::VERTICAL value
375 // bit 1 = the StyleWritingMode::INLINE_REVERSED value
376 // bit 2 = the StyleWritingMode::VERTICAL_LR value
377 // bit 3 = the StyleWritingMode::LINE_INVERTED value
378 // Not all of these combinations can actually be specified via CSS: there
379 // is no horizontal-bt writing-mode, and no text-orientation value that
380 // produces "inverted" text. (The former 'sideways-left' value, no longer
381 // in the spec, would have produced this in vertical-rl mode.)
382 static const mozilla::Side kLogicalInlineSides[][2] = {
383 {eSideLeft, eSideRight}, // horizontal-tb ltr
384 {eSideTop, eSideBottom}, // vertical-rl ltr
385 {eSideRight, eSideLeft}, // horizontal-tb rtl
386 {eSideBottom, eSideTop}, // vertical-rl rtl
387 {eSideRight, eSideLeft}, // (horizontal-bt) (inverted) ltr
388 {eSideTop, eSideBottom}, // sideways-lr rtl
389 {eSideLeft, eSideRight}, // (horizontal-bt) (inverted) rtl
390 {eSideBottom, eSideTop}, // sideways-lr ltr
391 {eSideLeft, eSideRight}, // horizontal-tb (inverted) rtl
392 {eSideTop, eSideBottom}, // vertical-rl (inverted) rtl
393 {eSideRight, eSideLeft}, // horizontal-tb (inverted) ltr
394 {eSideBottom, eSideTop}, // vertical-rl (inverted) ltr
395 {eSideLeft, eSideRight}, // (horizontal-bt) ltr
396 {eSideTop, eSideBottom}, // vertical-lr ltr
397 {eSideRight, eSideLeft}, // (horizontal-bt) rtl
398 {eSideBottom, eSideTop}, // vertical-lr rtl
401 // Inline axis sides depend on all three of writing-mode, text-orientation
402 // and direction, which are encoded in the StyleWritingMode::VERTICAL,
403 // StyleWritingMode::INLINE_REVERSED, StyleWritingMode::VERTICAL_LR and
404 // StyleWritingMode::LINE_INVERTED bits. Use these four bits to index into
405 // kLogicalInlineSides.
406 MOZ_ASSERT(StyleWritingMode::VERTICAL._0 == 0x01 &&
407 StyleWritingMode::INLINE_REVERSED._0 == 0x02 &&
408 StyleWritingMode::VERTICAL_LR._0 == 0x04 &&
409 StyleWritingMode::LINE_INVERTED._0 == 0x08,
410 "unexpected mask values");
411 int index = mWritingMode._0 & 0x0F;
412 return kLogicalInlineSides[index][aEdge];
416 * Returns the physical side corresponding to the specified logical side,
417 * given the current writing mode.
419 mozilla::Side PhysicalSide(LogicalSide aSide) const {
420 if (IsBlock(aSide)) {
421 MOZ_ASSERT(StyleWritingMode::VERTICAL._0 == 0x01 &&
422 StyleWritingMode::VERTICAL_LR._0 == 0x04,
423 "unexpected mask values");
424 const uint8_t wm =
425 ((mWritingMode & StyleWritingMode::VERTICAL_LR)._0 >> 1) |
426 (mWritingMode & StyleWritingMode::VERTICAL)._0;
427 return PhysicalSideForBlockAxis(wm, GetEdge(aSide));
430 return PhysicalSideForInlineAxis(GetEdge(aSide));
434 * Returns the logical side corresponding to the specified physical side,
435 * given the current writing mode.
436 * (This is the inverse of the PhysicalSide() method above.)
438 LogicalSide LogicalSideForPhysicalSide(mozilla::Side aSide) const {
439 // clang-format off
440 // indexes are four-bit values:
441 // bit 0 = the StyleWritingMode::VERTICAL value
442 // bit 1 = the StyleWritingMode::INLINE_REVERSED value
443 // bit 2 = the StyleWritingMode::VERTICAL_LR value
444 // bit 3 = the StyleWritingMode::LINE_INVERTED value
445 static const LogicalSide kPhysicalToLogicalSides[][4] = {
446 // top right
447 // bottom left
448 { eLogicalSideBStart, eLogicalSideIEnd,
449 eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb ltr
450 { eLogicalSideIStart, eLogicalSideBStart,
451 eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl ltr
452 { eLogicalSideBStart, eLogicalSideIStart,
453 eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb rtl
454 { eLogicalSideIEnd, eLogicalSideBStart,
455 eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl rtl
456 { eLogicalSideBEnd, eLogicalSideIStart,
457 eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) (inv) ltr
458 { eLogicalSideIStart, eLogicalSideBEnd,
459 eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr sw-left rtl
460 { eLogicalSideBEnd, eLogicalSideIEnd,
461 eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) (inv) rtl
462 { eLogicalSideIEnd, eLogicalSideBEnd,
463 eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr sw-left ltr
464 { eLogicalSideBStart, eLogicalSideIEnd,
465 eLogicalSideBEnd, eLogicalSideIStart }, // horizontal-tb (inv) rtl
466 { eLogicalSideIStart, eLogicalSideBStart,
467 eLogicalSideIEnd, eLogicalSideBEnd }, // vertical-rl sw-left rtl
468 { eLogicalSideBStart, eLogicalSideIStart,
469 eLogicalSideBEnd, eLogicalSideIEnd }, // horizontal-tb (inv) ltr
470 { eLogicalSideIEnd, eLogicalSideBStart,
471 eLogicalSideIStart, eLogicalSideBEnd }, // vertical-rl sw-left ltr
472 { eLogicalSideBEnd, eLogicalSideIEnd,
473 eLogicalSideBStart, eLogicalSideIStart }, // (horizontal-bt) ltr
474 { eLogicalSideIStart, eLogicalSideBEnd,
475 eLogicalSideIEnd, eLogicalSideBStart }, // vertical-lr ltr
476 { eLogicalSideBEnd, eLogicalSideIStart,
477 eLogicalSideBStart, eLogicalSideIEnd }, // (horizontal-bt) rtl
478 { eLogicalSideIEnd, eLogicalSideBEnd,
479 eLogicalSideIStart, eLogicalSideBStart }, // vertical-lr rtl
481 // clang-format on
483 MOZ_ASSERT(StyleWritingMode::VERTICAL._0 == 0x01 &&
484 StyleWritingMode::INLINE_REVERSED._0 == 0x02 &&
485 StyleWritingMode::VERTICAL_LR._0 == 0x04 &&
486 StyleWritingMode::LINE_INVERTED._0 == 0x08,
487 "unexpected mask values");
488 int index = mWritingMode._0 & 0x0F;
489 return kPhysicalToLogicalSides[index][aSide];
493 * Returns the logical side corresponding to the specified
494 * line-relative direction, given the current writing mode.
496 LogicalSide LogicalSideForLineRelativeDir(LineRelativeDir aDir) const {
497 auto side = static_cast<LogicalSide>(aDir);
498 if (IsInline(side)) {
499 return IsBidiLTR() ? side : GetOppositeSide(side);
501 return !IsLineInverted() ? side : GetOppositeSide(side);
505 * Default constructor gives us a horizontal, LTR writing mode.
506 * XXX We will probably eliminate this and require explicit initialization
507 * in all cases once transition is complete.
509 WritingMode() : mWritingMode{0} {}
512 * Construct writing mode based on a ComputedStyle.
514 explicit WritingMode(const ComputedStyle* aComputedStyle) {
515 NS_ASSERTION(aComputedStyle, "we need an ComputedStyle here");
516 mWritingMode = aComputedStyle->WritingMode();
520 * This function performs fixup for elements with 'unicode-bidi: plaintext',
521 * where inline directionality is derived from the Unicode bidi categories
522 * of the element's content, and not the CSS 'direction' property.
524 * The WritingMode constructor will have already incorporated the 'direction'
525 * property into our flag bits, so such elements need to use this method
526 * (after resolving the bidi level of their content) to update the direction
527 * bits as needed.
529 * If it turns out that our bidi direction already matches what plaintext
530 * resolution determined, there's nothing to do here. If it didn't (i.e. if
531 * the rtl-ness doesn't match), then we correct the direction by flipping the
532 * same bits that get flipped in the constructor's CSS 'direction'-based
533 * chunk.
535 * XXX change uint8_t to UBiDiLevel after bug 924851
537 void SetDirectionFromBidiLevel(mozilla::intl::BidiEmbeddingLevel level) {
538 if (level.IsRTL() == IsBidiLTR()) {
539 mWritingMode ^= StyleWritingMode::RTL | StyleWritingMode::INLINE_REVERSED;
544 * Compare two WritingModes for equality.
546 bool operator==(const WritingMode& aOther) const {
547 return mWritingMode == aOther.mWritingMode;
550 bool operator!=(const WritingMode& aOther) const {
551 return mWritingMode != aOther.mWritingMode;
555 * Check whether two modes are orthogonal to each other.
557 bool IsOrthogonalTo(const WritingMode& aOther) const {
558 return IsVertical() != aOther.IsVertical();
562 * Returns true if this WritingMode's aLogicalAxis has the same physical
563 * start side as the parallel axis of WritingMode |aOther|.
565 * @param aLogicalAxis The axis to compare from this WritingMode.
566 * @param aOther The other WritingMode (from which we'll choose the axis
567 * that's parallel to this WritingMode's aLogicalAxis, for
568 * comparison).
570 bool ParallelAxisStartsOnSameSide(LogicalAxis aLogicalAxis,
571 const WritingMode& aOther) const {
572 mozilla::Side myStartSide =
573 this->PhysicalSide(MakeLogicalSide(aLogicalAxis, eLogicalEdgeStart));
575 // Figure out which of aOther's axes is parallel to |this| WritingMode's
576 // aLogicalAxis, and get its physical start side as well.
577 LogicalAxis otherWMAxis = aOther.IsOrthogonalTo(*this)
578 ? GetOrthogonalAxis(aLogicalAxis)
579 : aLogicalAxis;
580 mozilla::Side otherWMStartSide =
581 aOther.PhysicalSide(MakeLogicalSide(otherWMAxis, eLogicalEdgeStart));
583 NS_ASSERTION(myStartSide % 2 == otherWMStartSide % 2,
584 "Should end up with sides in the same physical axis");
585 return myStartSide == otherWMStartSide;
588 uint8_t GetBits() const { return mWritingMode._0; }
590 private:
591 friend class LogicalPoint;
592 friend class LogicalSize;
593 friend struct LogicalSides;
594 friend class LogicalMargin;
595 friend class LogicalRect;
597 friend struct IPC::ParamTraits<WritingMode>;
598 // IMENotification cannot store this class directly since this has some
599 // constructors. Therefore, it stores mWritingMode and recreate the
600 // instance from it.
601 friend struct widget::IMENotification;
604 * Return a WritingMode representing an unknown value.
606 static inline WritingMode Unknown() {
607 return WritingMode(eUnknownWritingMode);
611 * Constructing a WritingMode with an arbitrary value is a private operation
612 * currently only used by the Unknown() and IgnoreSideways() methods.
614 explicit WritingMode(uint8_t aValue) : mWritingMode{aValue} {}
616 StyleWritingMode mWritingMode;
618 enum Masks {
619 // Masks for output enums
620 eInlineMask = 0x03, // VERTICAL | INLINE_REVERSED
621 eBlockMask = 0x05, // VERTICAL | VERTICAL_LR
625 inline std::ostream& operator<<(std::ostream& aStream, const WritingMode& aWM) {
626 return aStream << (aWM.IsVertical()
627 ? aWM.IsVerticalLR() ? aWM.IsBidiLTR()
628 ? aWM.IsSideways()
629 ? "sw-lr-ltr"
630 : "v-lr-ltr"
631 : aWM.IsSideways() ? "sw-lr-rtl"
632 : "v-lr-rtl"
633 : aWM.IsBidiLTR()
634 ? aWM.IsSideways() ? "sw-rl-ltr" : "v-rl-ltr"
635 : aWM.IsSideways() ? "sw-rl-rtl"
636 : "v-rl-rtl"
637 : aWM.IsBidiLTR() ? "h-ltr"
638 : "h-rtl");
642 * Logical-coordinate classes:
644 * There are three sets of coordinate space:
645 * - physical (top, left, bottom, right)
646 * relative to graphics coord system
647 * - flow-relative (block-start, inline-start, block-end, inline-end)
648 * relative to block/inline flow directions
649 * - line-relative (line-over, line-left, line-under, line-right)
650 * relative to glyph orientation / inline bidi directions
651 * See CSS3 Writing Modes for more information
652 * http://www.w3.org/TR/css3-writing-modes/#abstract-box
654 * For shorthand, B represents the block-axis
655 * I represents the inline-axis
657 * The flow-relative geometric classes store coords in flow-relative space.
658 * They use a private ns{Point,Size,Rect,Margin} member to store the actual
659 * coordinate values, but reinterpret them as logical instead of physical.
660 * This allows us to easily perform calculations in logical space (provided
661 * writing modes of the operands match), by simply mapping to nsPoint (etc)
662 * methods.
664 * Physical-coordinate accessors/setters are responsible to translate these
665 * internal logical values as necessary.
667 * In DEBUG builds, the logical types store their WritingMode and check
668 * that the same WritingMode is passed whenever callers ask them to do a
669 * writing-mode-dependent operation. Non-DEBUG builds do NOT check this,
670 * to avoid the overhead of storing WritingMode fields.
672 * Open question: do we need a different set optimized for line-relative
673 * math, for use in nsLineLayout and the like? Or is multiplying values
674 * by FlowRelativeToLineRelativeFactor() enough?
678 * Flow-relative point
680 class LogicalPoint {
681 public:
682 explicit LogicalPoint(WritingMode aWritingMode)
684 #ifdef DEBUG
685 mWritingMode(aWritingMode),
686 #endif
687 mPoint(0, 0) {
690 // Construct from a writing mode and individual coordinates (which MUST be
691 // values in that writing mode, NOT physical coordinates!)
692 LogicalPoint(WritingMode aWritingMode, nscoord aI, nscoord aB)
694 #ifdef DEBUG
695 mWritingMode(aWritingMode),
696 #endif
697 mPoint(aI, aB) {
700 // Construct from a writing mode and a physical point, within a given
701 // containing rectangle's size (defining the conversion between LTR
702 // and RTL coordinates, and between TTB and BTT coordinates).
703 LogicalPoint(WritingMode aWritingMode, const nsPoint& aPoint,
704 const nsSize& aContainerSize)
705 #ifdef DEBUG
706 : mWritingMode(aWritingMode)
707 #endif
709 if (aWritingMode.IsVertical()) {
710 I() = aWritingMode.IsInlineReversed() ? aContainerSize.height - aPoint.y
711 : aPoint.y;
712 B() = aWritingMode.IsVerticalLR() ? aPoint.x
713 : aContainerSize.width - aPoint.x;
714 } else {
715 I() = aWritingMode.IsInlineReversed() ? aContainerSize.width - aPoint.x
716 : aPoint.x;
717 B() = aPoint.y;
722 * Read-only (const) access to the logical coordinates.
724 nscoord I(WritingMode aWritingMode) const // inline-axis
726 CHECK_WRITING_MODE(aWritingMode);
727 return mPoint.x;
729 nscoord B(WritingMode aWritingMode) const // block-axis
731 CHECK_WRITING_MODE(aWritingMode);
732 return mPoint.y;
734 nscoord Pos(LogicalAxis aAxis, WritingMode aWM) const {
735 return aAxis == eLogicalAxisInline ? I(aWM) : B(aWM);
737 nscoord LineRelative(WritingMode aWritingMode,
738 const nsSize& aContainerSize) const // line-axis
740 CHECK_WRITING_MODE(aWritingMode);
741 if (aWritingMode.IsBidiLTR()) {
742 return I();
744 return (aWritingMode.IsVertical() ? aContainerSize.height
745 : aContainerSize.width) -
746 I();
750 * These non-const accessors return a reference (lvalue) that can be
751 * assigned to by callers.
753 nscoord& I(WritingMode aWritingMode) // inline-axis
755 CHECK_WRITING_MODE(aWritingMode);
756 return mPoint.x;
758 nscoord& B(WritingMode aWritingMode) // block-axis
760 CHECK_WRITING_MODE(aWritingMode);
761 return mPoint.y;
763 nscoord& Pos(LogicalAxis aAxis, WritingMode aWM) {
764 return aAxis == eLogicalAxisInline ? I(aWM) : B(aWM);
768 * Return a physical point corresponding to our logical coordinates,
769 * converted according to our writing mode.
771 nsPoint GetPhysicalPoint(WritingMode aWritingMode,
772 const nsSize& aContainerSize) const {
773 CHECK_WRITING_MODE(aWritingMode);
774 if (aWritingMode.IsVertical()) {
775 return nsPoint(
776 aWritingMode.IsVerticalLR() ? B() : aContainerSize.width - B(),
777 aWritingMode.IsInlineReversed() ? aContainerSize.height - I() : I());
778 } else {
779 return nsPoint(
780 aWritingMode.IsInlineReversed() ? aContainerSize.width - I() : I(),
781 B());
786 * Return the equivalent point in a different writing mode.
788 LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
789 const nsSize& aContainerSize) const {
790 CHECK_WRITING_MODE(aFromMode);
791 return aToMode == aFromMode
792 ? *this
793 : LogicalPoint(aToMode,
794 GetPhysicalPoint(aFromMode, aContainerSize),
795 aContainerSize);
798 bool operator==(const LogicalPoint& aOther) const {
799 CHECK_WRITING_MODE(aOther.GetWritingMode());
800 return mPoint == aOther.mPoint;
803 bool operator!=(const LogicalPoint& aOther) const {
804 CHECK_WRITING_MODE(aOther.GetWritingMode());
805 return mPoint != aOther.mPoint;
808 LogicalPoint operator+(const LogicalPoint& aOther) const {
809 CHECK_WRITING_MODE(aOther.GetWritingMode());
810 // In non-debug builds, LogicalPoint does not store the WritingMode,
811 // so the first parameter here (which will always be eUnknownWritingMode)
812 // is ignored.
813 return LogicalPoint(GetWritingMode(), mPoint.x + aOther.mPoint.x,
814 mPoint.y + aOther.mPoint.y);
817 LogicalPoint& operator+=(const LogicalPoint& aOther) {
818 CHECK_WRITING_MODE(aOther.GetWritingMode());
819 I() += aOther.I();
820 B() += aOther.B();
821 return *this;
824 LogicalPoint operator-(const LogicalPoint& aOther) const {
825 CHECK_WRITING_MODE(aOther.GetWritingMode());
826 // In non-debug builds, LogicalPoint does not store the WritingMode,
827 // so the first parameter here (which will always be eUnknownWritingMode)
828 // is ignored.
829 return LogicalPoint(GetWritingMode(), mPoint.x - aOther.mPoint.x,
830 mPoint.y - aOther.mPoint.y);
833 LogicalPoint& operator-=(const LogicalPoint& aOther) {
834 CHECK_WRITING_MODE(aOther.GetWritingMode());
835 I() -= aOther.I();
836 B() -= aOther.B();
837 return *this;
840 friend std::ostream& operator<<(std::ostream& aStream,
841 const LogicalPoint& aPoint) {
842 return aStream << aPoint.mPoint;
845 private:
846 friend class LogicalRect;
849 * NOTE that in non-DEBUG builds, GetWritingMode() always returns
850 * eUnknownWritingMode, as the current mode is not stored in the logical-
851 * geometry classes. Therefore, this method is private; it is used ONLY
852 * by the DEBUG-mode checking macros in this class and its friends;
853 * other code is not allowed to ask a logical point for its writing mode,
854 * as this info will simply not be available in non-DEBUG builds.
856 * Also, in non-DEBUG builds, CHECK_WRITING_MODE does nothing, and the
857 * WritingMode parameter to logical methods will generally be optimized
858 * away altogether.
860 #ifdef DEBUG
861 WritingMode GetWritingMode() const { return mWritingMode; }
862 #else
863 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
864 #endif
866 // We don't allow construction of a LogicalPoint with no writing mode.
867 LogicalPoint() = delete;
869 // Accessors that don't take or check a WritingMode value.
870 // These are for internal use only; they are called by methods that have
871 // themselves already checked the WritingMode passed by the caller.
872 nscoord I() const // inline-axis
874 return mPoint.x;
876 nscoord B() const // block-axis
878 return mPoint.y;
881 nscoord& I() // inline-axis
883 return mPoint.x;
885 nscoord& B() // block-axis
887 return mPoint.y;
890 #ifdef DEBUG
891 WritingMode mWritingMode;
892 #endif
894 // We use an nsPoint to hold the coordinates, but reinterpret its .x and .y
895 // fields as the inline and block directions. Hence, this is not exposed
896 // directly, but only through accessors that will map them according to the
897 // writing mode.
898 nsPoint mPoint;
902 * Flow-relative size
904 class LogicalSize {
905 public:
906 explicit LogicalSize(WritingMode aWritingMode)
908 #ifdef DEBUG
909 mWritingMode(aWritingMode),
910 #endif
911 mSize(0, 0) {
914 LogicalSize(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
916 #ifdef DEBUG
917 mWritingMode(aWritingMode),
918 #endif
919 mSize(aISize, aBSize) {
922 LogicalSize(WritingMode aWritingMode, const nsSize& aPhysicalSize)
923 #ifdef DEBUG
924 : mWritingMode(aWritingMode)
925 #endif
927 if (aWritingMode.IsVertical()) {
928 ISize() = aPhysicalSize.height;
929 BSize() = aPhysicalSize.width;
930 } else {
931 ISize() = aPhysicalSize.width;
932 BSize() = aPhysicalSize.height;
936 void SizeTo(WritingMode aWritingMode, nscoord aISize, nscoord aBSize) {
937 CHECK_WRITING_MODE(aWritingMode);
938 mSize.SizeTo(aISize, aBSize);
942 * Dimensions in logical and physical terms
944 nscoord ISize(WritingMode aWritingMode) const // inline-size
946 CHECK_WRITING_MODE(aWritingMode);
947 return mSize.width;
949 nscoord BSize(WritingMode aWritingMode) const // block-size
951 CHECK_WRITING_MODE(aWritingMode);
952 return mSize.height;
954 nscoord Size(LogicalAxis aAxis, WritingMode aWM) const {
955 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
958 nscoord Width(WritingMode aWritingMode) const {
959 CHECK_WRITING_MODE(aWritingMode);
960 return aWritingMode.IsVertical() ? BSize() : ISize();
962 nscoord Height(WritingMode aWritingMode) const {
963 CHECK_WRITING_MODE(aWritingMode);
964 return aWritingMode.IsVertical() ? ISize() : BSize();
968 * Writable references to the logical dimensions
970 nscoord& ISize(WritingMode aWritingMode) // inline-size
972 CHECK_WRITING_MODE(aWritingMode);
973 return mSize.width;
975 nscoord& BSize(WritingMode aWritingMode) // block-size
977 CHECK_WRITING_MODE(aWritingMode);
978 return mSize.height;
980 nscoord& Size(LogicalAxis aAxis, WritingMode aWM) {
981 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
985 * Return an nsSize containing our physical dimensions
987 nsSize GetPhysicalSize(WritingMode aWritingMode) const {
988 CHECK_WRITING_MODE(aWritingMode);
989 return aWritingMode.IsVertical() ? nsSize(BSize(), ISize())
990 : nsSize(ISize(), BSize());
994 * Return a LogicalSize representing this size in a different writing mode
996 LogicalSize ConvertTo(WritingMode aToMode, WritingMode aFromMode) const {
997 #ifdef DEBUG
998 // In DEBUG builds make sure to return a LogicalSize with the
999 // expected writing mode
1000 CHECK_WRITING_MODE(aFromMode);
1001 return aToMode == aFromMode
1002 ? *this
1003 : LogicalSize(aToMode, GetPhysicalSize(aFromMode));
1004 #else
1005 // optimization for non-DEBUG builds where LogicalSize doesn't store
1006 // the writing mode
1007 return (aToMode == aFromMode || !aToMode.IsOrthogonalTo(aFromMode))
1008 ? *this
1009 : LogicalSize(aToMode, BSize(), ISize());
1010 #endif
1014 * Test if a size is (0, 0).
1016 bool IsAllZero() const { return ISize() == 0 && BSize() == 0; }
1019 * Various binary operators on LogicalSize. These are valid ONLY for operands
1020 * that share the same writing mode.
1022 bool operator==(const LogicalSize& aOther) const {
1023 CHECK_WRITING_MODE(aOther.GetWritingMode());
1024 return mSize == aOther.mSize;
1027 bool operator!=(const LogicalSize& aOther) const {
1028 CHECK_WRITING_MODE(aOther.GetWritingMode());
1029 return mSize != aOther.mSize;
1032 LogicalSize operator+(const LogicalSize& aOther) const {
1033 CHECK_WRITING_MODE(aOther.GetWritingMode());
1034 return LogicalSize(GetWritingMode(), ISize() + aOther.ISize(),
1035 BSize() + aOther.BSize());
1037 LogicalSize& operator+=(const LogicalSize& aOther) {
1038 CHECK_WRITING_MODE(aOther.GetWritingMode());
1039 ISize() += aOther.ISize();
1040 BSize() += aOther.BSize();
1041 return *this;
1044 LogicalSize operator-(const LogicalSize& aOther) const {
1045 CHECK_WRITING_MODE(aOther.GetWritingMode());
1046 return LogicalSize(GetWritingMode(), ISize() - aOther.ISize(),
1047 BSize() - aOther.BSize());
1049 LogicalSize& operator-=(const LogicalSize& aOther) {
1050 CHECK_WRITING_MODE(aOther.GetWritingMode());
1051 ISize() -= aOther.ISize();
1052 BSize() -= aOther.BSize();
1053 return *this;
1056 friend std::ostream& operator<<(std::ostream& aStream,
1057 const LogicalSize& aSize) {
1058 return aStream << aSize.mSize;
1061 private:
1062 friend class LogicalRect;
1064 LogicalSize() = delete;
1066 #ifdef DEBUG
1067 WritingMode GetWritingMode() const { return mWritingMode; }
1068 #else
1069 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1070 #endif
1072 nscoord ISize() const // inline-size
1074 return mSize.width;
1076 nscoord BSize() const // block-size
1078 return mSize.height;
1081 nscoord& ISize() // inline-size
1083 return mSize.width;
1085 nscoord& BSize() // block-size
1087 return mSize.height;
1090 #ifdef DEBUG
1091 WritingMode mWritingMode;
1092 #endif
1093 nsSize mSize;
1097 * LogicalSides represents a set of logical sides.
1099 struct LogicalSides final {
1100 explicit LogicalSides(WritingMode aWritingMode)
1102 #ifdef DEBUG
1103 mWritingMode(aWritingMode),
1104 #endif
1105 mBits(0) {
1107 LogicalSides(WritingMode aWritingMode, LogicalSideBits aSideBits)
1109 #ifdef DEBUG
1110 mWritingMode(aWritingMode),
1111 #endif
1112 mBits(aSideBits) {
1113 MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
1115 bool IsEmpty() const { return mBits == 0; }
1116 bool BStart() const { return mBits & eLogicalSideBitsBStart; }
1117 bool BEnd() const { return mBits & eLogicalSideBitsBEnd; }
1118 bool IStart() const { return mBits & eLogicalSideBitsIStart; }
1119 bool IEnd() const { return mBits & eLogicalSideBitsIEnd; }
1120 bool Contains(LogicalSideBits aSideBits) const {
1121 MOZ_ASSERT((aSideBits & ~eLogicalSideBitsAll) == 0, "illegal side bits");
1122 return (mBits & aSideBits) == aSideBits;
1124 LogicalSides operator|(LogicalSides aOther) const {
1125 CHECK_WRITING_MODE(aOther.GetWritingMode());
1126 return *this | LogicalSideBits(aOther.mBits);
1128 LogicalSides operator|(LogicalSideBits aSideBits) const {
1129 return LogicalSides(GetWritingMode(), LogicalSideBits(mBits | aSideBits));
1131 LogicalSides& operator|=(LogicalSides aOther) {
1132 CHECK_WRITING_MODE(aOther.GetWritingMode());
1133 return *this |= LogicalSideBits(aOther.mBits);
1135 LogicalSides& operator|=(LogicalSideBits aSideBits) {
1136 mBits |= aSideBits;
1137 return *this;
1139 bool operator==(LogicalSides aOther) const {
1140 CHECK_WRITING_MODE(aOther.GetWritingMode());
1141 return mBits == aOther.mBits;
1143 bool operator!=(LogicalSides aOther) const {
1144 CHECK_WRITING_MODE(aOther.GetWritingMode());
1145 return !(*this == aOther);
1148 #ifdef DEBUG
1149 WritingMode GetWritingMode() const { return mWritingMode; }
1150 #else
1151 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1152 #endif
1154 private:
1155 #ifdef DEBUG
1156 WritingMode mWritingMode;
1157 #endif
1158 uint8_t mBits;
1162 * Flow-relative margin
1164 class LogicalMargin {
1165 public:
1166 explicit LogicalMargin(WritingMode aWritingMode)
1168 #ifdef DEBUG
1169 mWritingMode(aWritingMode),
1170 #endif
1171 mMargin(0, 0, 0, 0) {
1174 LogicalMargin(WritingMode aWritingMode, nscoord aBStart, nscoord aIEnd,
1175 nscoord aBEnd, nscoord aIStart)
1177 #ifdef DEBUG
1178 mWritingMode(aWritingMode),
1179 #endif
1180 mMargin(aBStart, aIEnd, aBEnd, aIStart) {
1183 LogicalMargin(WritingMode aWritingMode, const nsMargin& aPhysicalMargin)
1184 #ifdef DEBUG
1185 : mWritingMode(aWritingMode)
1186 #endif
1188 if (aWritingMode.IsVertical()) {
1189 if (aWritingMode.IsVerticalLR()) {
1190 mMargin.top = aPhysicalMargin.left;
1191 mMargin.bottom = aPhysicalMargin.right;
1192 } else {
1193 mMargin.top = aPhysicalMargin.right;
1194 mMargin.bottom = aPhysicalMargin.left;
1196 if (aWritingMode.IsInlineReversed()) {
1197 mMargin.left = aPhysicalMargin.bottom;
1198 mMargin.right = aPhysicalMargin.top;
1199 } else {
1200 mMargin.left = aPhysicalMargin.top;
1201 mMargin.right = aPhysicalMargin.bottom;
1203 } else {
1204 mMargin.top = aPhysicalMargin.top;
1205 mMargin.bottom = aPhysicalMargin.bottom;
1206 if (aWritingMode.IsInlineReversed()) {
1207 mMargin.left = aPhysicalMargin.right;
1208 mMargin.right = aPhysicalMargin.left;
1209 } else {
1210 mMargin.left = aPhysicalMargin.left;
1211 mMargin.right = aPhysicalMargin.right;
1216 nscoord IStart(WritingMode aWritingMode) const // inline-start margin
1218 CHECK_WRITING_MODE(aWritingMode);
1219 return mMargin.left;
1221 nscoord IEnd(WritingMode aWritingMode) const // inline-end margin
1223 CHECK_WRITING_MODE(aWritingMode);
1224 return mMargin.right;
1226 nscoord BStart(WritingMode aWritingMode) const // block-start margin
1228 CHECK_WRITING_MODE(aWritingMode);
1229 return mMargin.top;
1231 nscoord BEnd(WritingMode aWritingMode) const // block-end margin
1233 CHECK_WRITING_MODE(aWritingMode);
1234 return mMargin.bottom;
1236 nscoord Start(LogicalAxis aAxis, WritingMode aWM) const {
1237 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1239 nscoord End(LogicalAxis aAxis, WritingMode aWM) const {
1240 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1243 nscoord& IStart(WritingMode aWritingMode) // inline-start margin
1245 CHECK_WRITING_MODE(aWritingMode);
1246 return mMargin.left;
1248 nscoord& IEnd(WritingMode aWritingMode) // inline-end margin
1250 CHECK_WRITING_MODE(aWritingMode);
1251 return mMargin.right;
1253 nscoord& BStart(WritingMode aWritingMode) // block-start margin
1255 CHECK_WRITING_MODE(aWritingMode);
1256 return mMargin.top;
1258 nscoord& BEnd(WritingMode aWritingMode) // block-end margin
1260 CHECK_WRITING_MODE(aWritingMode);
1261 return mMargin.bottom;
1263 nscoord& Start(LogicalAxis aAxis, WritingMode aWM) {
1264 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1266 nscoord& End(LogicalAxis aAxis, WritingMode aWM) {
1267 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1270 nscoord IStartEnd(WritingMode aWritingMode) const // inline margins
1272 CHECK_WRITING_MODE(aWritingMode);
1273 return mMargin.LeftRight();
1275 nscoord BStartEnd(WritingMode aWritingMode) const // block margins
1277 CHECK_WRITING_MODE(aWritingMode);
1278 return mMargin.TopBottom();
1280 nscoord StartEnd(LogicalAxis aAxis, WritingMode aWM) const {
1281 return aAxis == eLogicalAxisInline ? IStartEnd(aWM) : BStartEnd(aWM);
1284 nscoord Side(LogicalSide aSide, WritingMode aWM) const {
1285 switch (aSide) {
1286 case eLogicalSideBStart:
1287 return BStart(aWM);
1288 case eLogicalSideBEnd:
1289 return BEnd(aWM);
1290 case eLogicalSideIStart:
1291 return IStart(aWM);
1292 case eLogicalSideIEnd:
1293 return IEnd(aWM);
1296 MOZ_ASSERT_UNREACHABLE("We should handle all sides!");
1297 return BStart(aWM);
1299 nscoord& Side(LogicalSide aSide, WritingMode aWM) {
1300 switch (aSide) {
1301 case eLogicalSideBStart:
1302 return BStart(aWM);
1303 case eLogicalSideBEnd:
1304 return BEnd(aWM);
1305 case eLogicalSideIStart:
1306 return IStart(aWM);
1307 case eLogicalSideIEnd:
1308 return IEnd(aWM);
1311 MOZ_ASSERT_UNREACHABLE("We should handle all sides!");
1312 return BStart(aWM);
1316 * Return margin values for line-relative sides, as defined in
1317 * http://www.w3.org/TR/css-writing-modes-3/#line-directions:
1319 * line-left
1320 * Nominally the side from which LTR text would start.
1321 * line-right
1322 * Nominally the side from which RTL text would start. (Opposite of
1323 * line-left.)
1325 nscoord LineLeft(WritingMode aWritingMode) const {
1326 // We don't need to CHECK_WRITING_MODE here because the IStart or IEnd
1327 // accessor that we call will do it.
1328 return aWritingMode.IsBidiLTR() ? IStart(aWritingMode) : IEnd(aWritingMode);
1330 nscoord LineRight(WritingMode aWritingMode) const {
1331 return aWritingMode.IsBidiLTR() ? IEnd(aWritingMode) : IStart(aWritingMode);
1335 * Return a LogicalSize representing the total size of the inline-
1336 * and block-dimension margins.
1338 LogicalSize Size(WritingMode aWritingMode) const {
1339 CHECK_WRITING_MODE(aWritingMode);
1340 return LogicalSize(aWritingMode, IStartEnd(), BStartEnd());
1344 * Return a LogicalPoint representing an offset to the start-sides, i.e.
1345 * inline-start and block-start.
1347 LogicalPoint StartOffset(WritingMode aWritingMode) const {
1348 CHECK_WRITING_MODE(aWritingMode);
1349 return LogicalPoint(aWritingMode, IStart(), BStart());
1353 * Accessors for physical margins, using our writing mode to convert from
1354 * logical values.
1356 nscoord Top(WritingMode aWritingMode) const {
1357 CHECK_WRITING_MODE(aWritingMode);
1358 return aWritingMode.IsVertical()
1359 ? (aWritingMode.IsInlineReversed() ? IEnd() : IStart())
1360 : BStart();
1363 nscoord Bottom(WritingMode aWritingMode) const {
1364 CHECK_WRITING_MODE(aWritingMode);
1365 return aWritingMode.IsVertical()
1366 ? (aWritingMode.IsInlineReversed() ? IStart() : IEnd())
1367 : BEnd();
1370 nscoord Left(WritingMode aWritingMode) const {
1371 CHECK_WRITING_MODE(aWritingMode);
1372 return aWritingMode.IsVertical()
1373 ? (aWritingMode.IsVerticalLR() ? BStart() : BEnd())
1374 : (aWritingMode.IsInlineReversed() ? IEnd() : IStart());
1377 nscoord Right(WritingMode aWritingMode) const {
1378 CHECK_WRITING_MODE(aWritingMode);
1379 return aWritingMode.IsVertical()
1380 ? (aWritingMode.IsVerticalLR() ? BEnd() : BStart())
1381 : (aWritingMode.IsInlineReversed() ? IStart() : IEnd());
1384 nscoord LeftRight(WritingMode aWritingMode) const {
1385 CHECK_WRITING_MODE(aWritingMode);
1386 return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
1389 nscoord TopBottom(WritingMode aWritingMode) const {
1390 CHECK_WRITING_MODE(aWritingMode);
1391 return aWritingMode.IsVertical() ? IStartEnd() : BStartEnd();
1394 void SizeTo(WritingMode aWritingMode, nscoord aBStart, nscoord aIEnd,
1395 nscoord aBEnd, nscoord aIStart) {
1396 CHECK_WRITING_MODE(aWritingMode);
1397 mMargin.SizeTo(aBStart, aIEnd, aBEnd, aIStart);
1401 * Return an nsMargin containing our physical coordinates
1403 nsMargin GetPhysicalMargin(WritingMode aWritingMode) const {
1404 CHECK_WRITING_MODE(aWritingMode);
1405 return aWritingMode.IsVertical()
1406 ? (aWritingMode.IsVerticalLR()
1407 ? (aWritingMode.IsInlineReversed()
1408 ? nsMargin(IEnd(), BEnd(), IStart(), BStart())
1409 : nsMargin(IStart(), BEnd(), IEnd(), BStart()))
1410 : (aWritingMode.IsInlineReversed()
1411 ? nsMargin(IEnd(), BStart(), IStart(), BEnd())
1412 : nsMargin(IStart(), BStart(), IEnd(), BEnd())))
1413 : (aWritingMode.IsInlineReversed()
1414 ? nsMargin(BStart(), IStart(), BEnd(), IEnd())
1415 : nsMargin(BStart(), IEnd(), BEnd(), IStart()));
1419 * Return a LogicalMargin representing this margin in a different
1420 * writing mode
1422 LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const {
1423 CHECK_WRITING_MODE(aFromMode);
1424 return aToMode == aFromMode
1425 ? *this
1426 : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
1429 LogicalMargin& ApplySkipSides(LogicalSides aSkipSides) {
1430 CHECK_WRITING_MODE(aSkipSides.GetWritingMode());
1431 if (aSkipSides.BStart()) {
1432 BStart() = 0;
1434 if (aSkipSides.BEnd()) {
1435 BEnd() = 0;
1437 if (aSkipSides.IStart()) {
1438 IStart() = 0;
1440 if (aSkipSides.IEnd()) {
1441 IEnd() = 0;
1443 return *this;
1446 bool IsAllZero() const {
1447 return (mMargin.left == 0 && mMargin.top == 0 && mMargin.right == 0 &&
1448 mMargin.bottom == 0);
1451 bool operator==(const LogicalMargin& aMargin) const {
1452 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1453 return mMargin == aMargin.mMargin;
1456 bool operator!=(const LogicalMargin& aMargin) const {
1457 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1458 return mMargin != aMargin.mMargin;
1461 LogicalMargin operator+(const LogicalMargin& aMargin) const {
1462 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1463 return LogicalMargin(GetWritingMode(), BStart() + aMargin.BStart(),
1464 IEnd() + aMargin.IEnd(), BEnd() + aMargin.BEnd(),
1465 IStart() + aMargin.IStart());
1468 LogicalMargin operator+=(const LogicalMargin& aMargin) {
1469 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1470 mMargin += aMargin.mMargin;
1471 return *this;
1474 LogicalMargin operator-(const LogicalMargin& aMargin) const {
1475 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1476 return LogicalMargin(GetWritingMode(), BStart() - aMargin.BStart(),
1477 IEnd() - aMargin.IEnd(), BEnd() - aMargin.BEnd(),
1478 IStart() - aMargin.IStart());
1481 friend std::ostream& operator<<(std::ostream& aStream,
1482 const LogicalMargin& aMargin) {
1483 return aStream << aMargin.mMargin;
1486 private:
1487 friend class LogicalRect;
1489 LogicalMargin() = delete;
1491 #ifdef DEBUG
1492 WritingMode GetWritingMode() const { return mWritingMode; }
1493 #else
1494 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1495 #endif
1497 nscoord IStart() const // inline-start margin
1499 return mMargin.left;
1501 nscoord IEnd() const // inline-end margin
1503 return mMargin.right;
1505 nscoord BStart() const // block-start margin
1507 return mMargin.top;
1509 nscoord BEnd() const // block-end margin
1511 return mMargin.bottom;
1514 nscoord& IStart() // inline-start margin
1516 return mMargin.left;
1518 nscoord& IEnd() // inline-end margin
1520 return mMargin.right;
1522 nscoord& BStart() // block-start margin
1524 return mMargin.top;
1526 nscoord& BEnd() // block-end margin
1528 return mMargin.bottom;
1531 nscoord IStartEnd() const // inline margins
1533 return mMargin.LeftRight();
1535 nscoord BStartEnd() const // block margins
1537 return mMargin.TopBottom();
1540 #ifdef DEBUG
1541 WritingMode mWritingMode;
1542 #endif
1543 nsMargin mMargin;
1547 * Flow-relative rectangle
1549 class LogicalRect {
1550 public:
1551 explicit LogicalRect(WritingMode aWritingMode)
1553 #ifdef DEBUG
1554 mWritingMode(aWritingMode),
1555 #endif
1556 mIStart(0),
1557 mBStart(0),
1558 mISize(0),
1559 mBSize(0) {
1562 LogicalRect(WritingMode aWritingMode, nscoord aIStart, nscoord aBStart,
1563 nscoord aISize, nscoord aBSize)
1565 #ifdef DEBUG
1566 mWritingMode(aWritingMode),
1567 #endif
1568 mIStart(aIStart),
1569 mBStart(aBStart),
1570 mISize(aISize),
1571 mBSize(aBSize) {
1574 LogicalRect(WritingMode aWritingMode, const LogicalPoint& aOrigin,
1575 const LogicalSize& aSize)
1577 #ifdef DEBUG
1578 mWritingMode(aWritingMode),
1579 #endif
1580 mIStart(aOrigin.mPoint.x),
1581 mBStart(aOrigin.mPoint.y),
1582 mISize(aSize.mSize.width),
1583 mBSize(aSize.mSize.height) {
1584 CHECK_WRITING_MODE(aOrigin.GetWritingMode());
1585 CHECK_WRITING_MODE(aSize.GetWritingMode());
1588 LogicalRect(WritingMode aWritingMode, const nsRect& aRect,
1589 const nsSize& aContainerSize)
1590 #ifdef DEBUG
1591 : mWritingMode(aWritingMode)
1592 #endif
1594 if (aWritingMode.IsVertical()) {
1595 mBStart = aWritingMode.IsVerticalLR()
1596 ? aRect.X()
1597 : aContainerSize.width - aRect.XMost();
1598 mIStart = aWritingMode.IsInlineReversed()
1599 ? aContainerSize.height - aRect.YMost()
1600 : aRect.Y();
1601 mBSize = aRect.Width();
1602 mISize = aRect.Height();
1603 } else {
1604 mIStart = aWritingMode.IsInlineReversed()
1605 ? aContainerSize.width - aRect.XMost()
1606 : aRect.X();
1607 mBStart = aRect.Y();
1608 mISize = aRect.Width();
1609 mBSize = aRect.Height();
1614 * Inline- and block-dimension geometry.
1616 nscoord IStart(WritingMode aWritingMode) const // inline-start edge
1618 CHECK_WRITING_MODE(aWritingMode);
1619 return mIStart;
1621 nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
1623 CHECK_WRITING_MODE(aWritingMode);
1624 return mIStart + mISize;
1626 nscoord ISize(WritingMode aWritingMode) const // inline-size
1628 CHECK_WRITING_MODE(aWritingMode);
1629 return mISize;
1632 nscoord BStart(WritingMode aWritingMode) const // block-start edge
1634 CHECK_WRITING_MODE(aWritingMode);
1635 return mBStart;
1637 nscoord BEnd(WritingMode aWritingMode) const // block-end edge
1639 CHECK_WRITING_MODE(aWritingMode);
1640 return mBStart + mBSize;
1642 nscoord BSize(WritingMode aWritingMode) const // block-size
1644 CHECK_WRITING_MODE(aWritingMode);
1645 return mBSize;
1648 nscoord Start(LogicalAxis aAxis, WritingMode aWM) const {
1649 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1651 nscoord End(LogicalAxis aAxis, WritingMode aWM) const {
1652 return aAxis == eLogicalAxisInline ? IEnd(aWM) : BEnd(aWM);
1654 nscoord Size(LogicalAxis aAxis, WritingMode aWM) const {
1655 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
1659 * Writable (reference) accessors are only available for the basic logical
1660 * fields (Start and Size), not derivatives like End.
1662 nscoord& IStart(WritingMode aWritingMode) // inline-start edge
1664 CHECK_WRITING_MODE(aWritingMode);
1665 return mIStart;
1667 nscoord& ISize(WritingMode aWritingMode) // inline-size
1669 CHECK_WRITING_MODE(aWritingMode);
1670 return mISize;
1672 nscoord& BStart(WritingMode aWritingMode) // block-start edge
1674 CHECK_WRITING_MODE(aWritingMode);
1675 return mBStart;
1677 nscoord& BSize(WritingMode aWritingMode) // block-size
1679 CHECK_WRITING_MODE(aWritingMode);
1680 return mBSize;
1682 nscoord& Start(LogicalAxis aAxis, WritingMode aWM) {
1683 return aAxis == eLogicalAxisInline ? IStart(aWM) : BStart(aWM);
1685 nscoord& Size(LogicalAxis aAxis, WritingMode aWM) {
1686 return aAxis == eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
1690 * Accessors for line-relative coordinates
1692 nscoord LineLeft(WritingMode aWritingMode,
1693 const nsSize& aContainerSize) const {
1694 CHECK_WRITING_MODE(aWritingMode);
1695 if (aWritingMode.IsBidiLTR()) {
1696 return IStart();
1698 nscoord containerISize = aWritingMode.IsVertical() ? aContainerSize.height
1699 : aContainerSize.width;
1700 return containerISize - IEnd();
1702 nscoord LineRight(WritingMode aWritingMode,
1703 const nsSize& aContainerSize) const {
1704 CHECK_WRITING_MODE(aWritingMode);
1705 if (aWritingMode.IsBidiLTR()) {
1706 return IEnd();
1708 nscoord containerISize = aWritingMode.IsVertical() ? aContainerSize.height
1709 : aContainerSize.width;
1710 return containerISize - IStart();
1714 * Physical coordinates of the rect.
1716 nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const {
1717 CHECK_WRITING_MODE(aWritingMode);
1718 if (aWritingMode.IsVertical()) {
1719 return aWritingMode.IsVerticalLR() ? mBStart : aContainerWidth - BEnd();
1721 return aWritingMode.IsInlineReversed() ? aContainerWidth - IEnd() : mIStart;
1724 nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const {
1725 CHECK_WRITING_MODE(aWritingMode);
1726 if (aWritingMode.IsVertical()) {
1727 return aWritingMode.IsInlineReversed() ? aContainerHeight - IEnd()
1728 : mIStart;
1730 return mBStart;
1733 nscoord Width(WritingMode aWritingMode) const {
1734 CHECK_WRITING_MODE(aWritingMode);
1735 return aWritingMode.IsVertical() ? mBSize : mISize;
1738 nscoord Height(WritingMode aWritingMode) const {
1739 CHECK_WRITING_MODE(aWritingMode);
1740 return aWritingMode.IsVertical() ? mISize : mBSize;
1743 nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const {
1744 CHECK_WRITING_MODE(aWritingMode);
1745 if (aWritingMode.IsVertical()) {
1746 return aWritingMode.IsVerticalLR() ? BEnd() : aContainerWidth - mBStart;
1748 return aWritingMode.IsInlineReversed() ? aContainerWidth - mIStart : IEnd();
1751 nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const {
1752 CHECK_WRITING_MODE(aWritingMode);
1753 if (aWritingMode.IsVertical()) {
1754 return aWritingMode.IsInlineReversed() ? aContainerHeight - mIStart
1755 : IEnd();
1757 return BEnd();
1760 bool IsEmpty() const { return mISize <= 0 || mBSize <= 0; }
1762 bool IsAllZero() const {
1763 return (mIStart == 0 && mBStart == 0 && mISize == 0 && mBSize == 0);
1766 bool IsZeroSize() const { return (mISize == 0 && mBSize == 0); }
1768 void SetEmpty() { mISize = mBSize = 0; }
1770 bool IsEqualEdges(const LogicalRect aOther) const {
1771 CHECK_WRITING_MODE(aOther.GetWritingMode());
1772 bool result = mIStart == aOther.mIStart && mBStart == aOther.mBStart &&
1773 mISize == aOther.mISize && mBSize == aOther.mBSize;
1775 // We want the same result as nsRect, so assert we get it.
1776 MOZ_ASSERT(result ==
1777 nsRect(mIStart, mBStart, mISize, mBSize)
1778 .IsEqualEdges(nsRect(aOther.mIStart, aOther.mBStart,
1779 aOther.mISize, aOther.mBSize)));
1780 return result;
1783 LogicalPoint Origin(WritingMode aWritingMode) const {
1784 CHECK_WRITING_MODE(aWritingMode);
1785 return LogicalPoint(aWritingMode, IStart(), BStart());
1787 void SetOrigin(WritingMode aWritingMode, const LogicalPoint& aPoint) {
1788 IStart(aWritingMode) = aPoint.I(aWritingMode);
1789 BStart(aWritingMode) = aPoint.B(aWritingMode);
1792 LogicalSize Size(WritingMode aWritingMode) const {
1793 CHECK_WRITING_MODE(aWritingMode);
1794 return LogicalSize(aWritingMode, ISize(), BSize());
1797 LogicalRect operator+(const LogicalPoint& aPoint) const {
1798 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1799 return LogicalRect(GetWritingMode(), IStart() + aPoint.I(),
1800 BStart() + aPoint.B(), ISize(), BSize());
1803 LogicalRect& operator+=(const LogicalPoint& aPoint) {
1804 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1805 mIStart += aPoint.mPoint.x;
1806 mBStart += aPoint.mPoint.y;
1807 return *this;
1810 LogicalRect operator-(const LogicalPoint& aPoint) const {
1811 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1812 return LogicalRect(GetWritingMode(), IStart() - aPoint.I(),
1813 BStart() - aPoint.B(), ISize(), BSize());
1816 LogicalRect& operator-=(const LogicalPoint& aPoint) {
1817 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1818 mIStart -= aPoint.mPoint.x;
1819 mBStart -= aPoint.mPoint.y;
1820 return *this;
1823 void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta) {
1824 CHECK_WRITING_MODE(aWritingMode);
1825 CHECK_WRITING_MODE(aDelta.GetWritingMode());
1826 IStart() += aDelta.I();
1827 BStart() += aDelta.B();
1830 void Inflate(nscoord aD) {
1831 #ifdef DEBUG
1832 // Compute using nsRect and assert the results match
1833 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1834 rectDebug.Inflate(aD);
1835 #endif
1836 mIStart -= aD;
1837 mBStart -= aD;
1838 mISize += 2 * aD;
1839 mBSize += 2 * aD;
1840 MOZ_ASSERT(
1841 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1843 void Inflate(nscoord aDI, nscoord aDB) {
1844 #ifdef DEBUG
1845 // Compute using nsRect and assert the results match
1846 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1847 rectDebug.Inflate(aDI, aDB);
1848 #endif
1849 mIStart -= aDI;
1850 mBStart -= aDB;
1851 mISize += 2 * aDI;
1852 mBSize += 2 * aDB;
1853 MOZ_ASSERT(
1854 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1856 void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin) {
1857 CHECK_WRITING_MODE(aWritingMode);
1858 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1859 #ifdef DEBUG
1860 // Compute using nsRect and assert the results match
1861 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1862 rectDebug.Inflate(aMargin.mMargin);
1863 #endif
1864 mIStart -= aMargin.mMargin.left;
1865 mBStart -= aMargin.mMargin.top;
1866 mISize += aMargin.mMargin.LeftRight();
1867 mBSize += aMargin.mMargin.TopBottom();
1868 MOZ_ASSERT(
1869 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1872 void Deflate(nscoord aD) {
1873 #ifdef DEBUG
1874 // Compute using nsRect and assert the results match
1875 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1876 rectDebug.Deflate(aD);
1877 #endif
1878 mIStart += aD;
1879 mBStart += aD;
1880 mISize = std::max(0, mISize - 2 * aD);
1881 mBSize = std::max(0, mBSize - 2 * aD);
1882 MOZ_ASSERT(
1883 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1885 void Deflate(nscoord aDI, nscoord aDB) {
1886 #ifdef DEBUG
1887 // Compute using nsRect and assert the results match
1888 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1889 rectDebug.Deflate(aDI, aDB);
1890 #endif
1891 mIStart += aDI;
1892 mBStart += aDB;
1893 mISize = std::max(0, mISize - 2 * aDI);
1894 mBSize = std::max(0, mBSize - 2 * aDB);
1895 MOZ_ASSERT(
1896 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1898 void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin) {
1899 CHECK_WRITING_MODE(aWritingMode);
1900 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1901 #ifdef DEBUG
1902 // Compute using nsRect and assert the results match
1903 nsRect rectDebug(mIStart, mBStart, mISize, mBSize);
1904 rectDebug.Deflate(aMargin.mMargin);
1905 #endif
1906 mIStart += aMargin.mMargin.left;
1907 mBStart += aMargin.mMargin.top;
1908 mISize = std::max(0, mISize - aMargin.mMargin.LeftRight());
1909 mBSize = std::max(0, mBSize - aMargin.mMargin.TopBottom());
1910 MOZ_ASSERT(
1911 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1915 * Return an nsRect containing our physical coordinates within the given
1916 * container size.
1918 nsRect GetPhysicalRect(WritingMode aWritingMode,
1919 const nsSize& aContainerSize) const {
1920 CHECK_WRITING_MODE(aWritingMode);
1921 if (aWritingMode.IsVertical()) {
1922 return nsRect(aWritingMode.IsVerticalLR() ? BStart()
1923 : aContainerSize.width - BEnd(),
1924 aWritingMode.IsInlineReversed()
1925 ? aContainerSize.height - IEnd()
1926 : IStart(),
1927 BSize(), ISize());
1928 } else {
1929 return nsRect(aWritingMode.IsInlineReversed()
1930 ? aContainerSize.width - IEnd()
1931 : IStart(),
1932 BStart(), ISize(), BSize());
1937 * Return a LogicalRect representing this rect in a different writing mode
1939 LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode,
1940 const nsSize& aContainerSize) const {
1941 CHECK_WRITING_MODE(aFromMode);
1942 return aToMode == aFromMode
1943 ? *this
1944 : LogicalRect(aToMode,
1945 GetPhysicalRect(aFromMode, aContainerSize),
1946 aContainerSize);
1950 * Set *this to be the rectangle containing the intersection of aRect1
1951 * and aRect2, return whether the intersection is non-empty.
1953 bool IntersectRect(const LogicalRect& aRect1, const LogicalRect& aRect2) {
1954 CHECK_WRITING_MODE(aRect1.mWritingMode);
1955 CHECK_WRITING_MODE(aRect2.mWritingMode);
1956 #ifdef DEBUG
1957 // Compute using nsRect and assert the results match
1958 nsRect rectDebug;
1959 rectDebug.IntersectRect(
1960 nsRect(aRect1.mIStart, aRect1.mBStart, aRect1.mISize, aRect1.mBSize),
1961 nsRect(aRect2.mIStart, aRect2.mBStart, aRect2.mISize, aRect2.mBSize));
1962 #endif
1964 nscoord iEnd = std::min(aRect1.IEnd(), aRect2.IEnd());
1965 mIStart = std::max(aRect1.mIStart, aRect2.mIStart);
1966 mISize = iEnd - mIStart;
1968 nscoord bEnd = std::min(aRect1.BEnd(), aRect2.BEnd());
1969 mBStart = std::max(aRect1.mBStart, aRect2.mBStart);
1970 mBSize = bEnd - mBStart;
1972 if (mISize < 0 || mBSize < 0) {
1973 mISize = 0;
1974 mBSize = 0;
1977 MOZ_ASSERT(
1978 (rectDebug.IsEmpty() && (mISize == 0 || mBSize == 0)) ||
1979 rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
1980 return mISize > 0 && mBSize > 0;
1983 friend std::ostream& operator<<(std::ostream& aStream,
1984 const LogicalRect& aRect) {
1985 return aStream << '(' << aRect.IStart() << ',' << aRect.BStart() << ','
1986 << aRect.ISize() << ',' << aRect.BSize() << ')';
1989 private:
1990 LogicalRect() = delete;
1992 #ifdef DEBUG
1993 WritingMode GetWritingMode() const { return mWritingMode; }
1994 #else
1995 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1996 #endif
1998 nscoord IStart() const // inline-start edge
2000 return mIStart;
2002 nscoord IEnd() const // inline-end edge
2004 return mIStart + mISize;
2006 nscoord ISize() const // inline-size
2008 return mISize;
2011 nscoord BStart() const // block-start edge
2013 return mBStart;
2015 nscoord BEnd() const // block-end edge
2017 return mBStart + mBSize;
2019 nscoord BSize() const // block-size
2021 return mBSize;
2024 nscoord& IStart() // inline-start edge
2026 return mIStart;
2028 nscoord& ISize() // inline-size
2030 return mISize;
2032 nscoord& BStart() // block-start edge
2034 return mBStart;
2036 nscoord& BSize() // block-size
2038 return mBSize;
2041 #ifdef DEBUG
2042 WritingMode mWritingMode;
2043 #endif
2044 // Inline- and block-geometry dimension
2045 nscoord mIStart; // inline-start edge
2046 nscoord mBStart; // block-start edge
2047 nscoord mISize; // inline-size
2048 nscoord mBSize; // block-size
2051 template <typename T>
2052 const T& StyleRect<T>::Get(WritingMode aWM, LogicalSide aSide) const {
2053 return Get(aWM.PhysicalSide(aSide));
2056 template <typename T>
2057 const T& StyleRect<T>::GetIStart(WritingMode aWM) const {
2058 return Get(aWM, eLogicalSideIStart);
2061 template <typename T>
2062 const T& StyleRect<T>::GetBStart(WritingMode aWM) const {
2063 return Get(aWM, eLogicalSideBStart);
2066 template <typename T>
2067 const T& StyleRect<T>::GetIEnd(WritingMode aWM) const {
2068 return Get(aWM, eLogicalSideIEnd);
2071 template <typename T>
2072 const T& StyleRect<T>::GetBEnd(WritingMode aWM) const {
2073 return Get(aWM, eLogicalSideBEnd);
2076 template <typename T>
2077 T& StyleRect<T>::Get(WritingMode aWM, LogicalSide aSide) {
2078 return Get(aWM.PhysicalSide(aSide));
2081 template <typename T>
2082 T& StyleRect<T>::GetIStart(WritingMode aWM) {
2083 return Get(aWM, eLogicalSideIStart);
2086 template <typename T>
2087 T& StyleRect<T>::GetBStart(WritingMode aWM) {
2088 return Get(aWM, eLogicalSideBStart);
2091 template <typename T>
2092 T& StyleRect<T>::GetIEnd(WritingMode aWM) {
2093 return Get(aWM, eLogicalSideIEnd);
2096 template <typename T>
2097 T& StyleRect<T>::GetBEnd(WritingMode aWM) {
2098 return Get(aWM, eLogicalSideBEnd);
2101 template <typename T>
2102 const T& StyleRect<T>::Start(mozilla::LogicalAxis aAxis,
2103 mozilla::WritingMode aWM) const {
2104 return Get(aWM, aAxis == mozilla::eLogicalAxisInline
2105 ? mozilla::eLogicalSideIStart
2106 : mozilla::eLogicalSideBStart);
2109 template <typename T>
2110 const T& StyleRect<T>::End(mozilla::LogicalAxis aAxis,
2111 mozilla::WritingMode aWM) const {
2112 return Get(aWM, aAxis == mozilla::eLogicalAxisInline
2113 ? mozilla::eLogicalSideIEnd
2114 : mozilla::eLogicalSideBEnd);
2117 inline AspectRatio AspectRatio::ConvertToWritingMode(
2118 const WritingMode& aWM) const {
2119 return aWM.IsVertical() ? Inverted() : *this;
2122 } // namespace mozilla
2124 // Definitions of inline methods for nsStylePosition, declared in
2125 // nsStyleStruct.h but not defined there because they need WritingMode.
2126 inline const mozilla::StyleSize& nsStylePosition::ISize(WritingMode aWM) const {
2127 return aWM.IsVertical() ? mHeight : mWidth;
2129 inline const mozilla::StyleSize& nsStylePosition::MinISize(
2130 WritingMode aWM) const {
2131 return aWM.IsVertical() ? mMinHeight : mMinWidth;
2133 inline const mozilla::StyleMaxSize& nsStylePosition::MaxISize(
2134 WritingMode aWM) const {
2135 return aWM.IsVertical() ? mMaxHeight : mMaxWidth;
2137 inline const mozilla::StyleSize& nsStylePosition::BSize(WritingMode aWM) const {
2138 return aWM.IsVertical() ? mWidth : mHeight;
2140 inline const mozilla::StyleSize& nsStylePosition::MinBSize(
2141 WritingMode aWM) const {
2142 return aWM.IsVertical() ? mMinWidth : mMinHeight;
2144 inline const mozilla::StyleMaxSize& nsStylePosition::MaxBSize(
2145 WritingMode aWM) const {
2146 return aWM.IsVertical() ? mMaxWidth : mMaxHeight;
2148 inline const mozilla::StyleSize& nsStylePosition::Size(
2149 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2150 return aAxis == mozilla::eLogicalAxisInline ? ISize(aWM) : BSize(aWM);
2152 inline const mozilla::StyleSize& nsStylePosition::MinSize(
2153 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2154 return aAxis == mozilla::eLogicalAxisInline ? MinISize(aWM) : MinBSize(aWM);
2156 inline const mozilla::StyleMaxSize& nsStylePosition::MaxSize(
2157 mozilla::LogicalAxis aAxis, WritingMode aWM) const {
2158 return aAxis == mozilla::eLogicalAxisInline ? MaxISize(aWM) : MaxBSize(aWM);
2161 inline bool nsStylePosition::ISizeDependsOnContainer(WritingMode aWM) const {
2162 const auto& iSize = ISize(aWM);
2163 return iSize.IsAuto() || ISizeCoordDependsOnContainer(iSize);
2165 inline bool nsStylePosition::MinISizeDependsOnContainer(WritingMode aWM) const {
2166 // NOTE: For a flex item, "min-inline-size:auto" is supposed to behave like
2167 // "min-content", which does depend on the container, so you might think we'd
2168 // need a special case for "flex item && min-inline-size:auto" here. However,
2169 // we don't actually need that special-case code, because flex items are
2170 // explicitly supposed to *ignore* their min-inline-size (i.e. behave like
2171 // it's 0) until the flex container explicitly considers it. So -- since the
2172 // flex container doesn't rely on this method, we don't need to worry about
2173 // special behavior for flex items' "min-inline-size:auto" values here.
2174 return ISizeCoordDependsOnContainer(MinISize(aWM));
2176 inline bool nsStylePosition::MaxISizeDependsOnContainer(WritingMode aWM) const {
2177 // NOTE: The comment above MinISizeDependsOnContainer about flex items
2178 // applies here, too.
2179 return ISizeCoordDependsOnContainer(MaxISize(aWM));
2181 // Note that these functions count `auto` as depending on the container
2182 // since that's the case for absolutely positioned elements.
2183 // However, some callers do not care about this case and should check
2184 // for it, since it is the most common case.
2185 // FIXME: We should probably change the assumption to be the other way
2186 // around.
2187 inline bool nsStylePosition::BSizeDependsOnContainer(WritingMode aWM) const {
2188 const auto& bSize = BSize(aWM);
2189 return bSize.BehavesLikeInitialValueOnBlockAxis() ||
2190 BSizeCoordDependsOnContainer(bSize);
2192 inline bool nsStylePosition::MinBSizeDependsOnContainer(WritingMode aWM) const {
2193 return BSizeCoordDependsOnContainer(MinBSize(aWM));
2195 inline bool nsStylePosition::MaxBSizeDependsOnContainer(WritingMode aWM) const {
2196 return BSizeCoordDependsOnContainer(MaxBSize(aWM));
2199 inline bool nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const {
2200 return mMargin.GetBStart(aWM).IsAuto() || mMargin.GetBEnd(aWM).IsAuto();
2203 inline bool nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const {
2204 return mMargin.GetIStart(aWM).IsAuto() || mMargin.GetIEnd(aWM).IsAuto();
2206 inline bool nsStyleMargin::HasAuto(mozilla::LogicalAxis aAxis,
2207 mozilla::WritingMode aWM) const {
2208 return aAxis == mozilla::eLogicalAxisInline ? HasInlineAxisAuto(aWM)
2209 : HasBlockAxisAuto(aWM);
2212 inline mozilla::StyleAlignFlags nsStylePosition::UsedSelfAlignment(
2213 mozilla::LogicalAxis aAxis, const mozilla::ComputedStyle* aParent) const {
2214 return aAxis == mozilla::eLogicalAxisBlock ? UsedAlignSelf(aParent)._0
2215 : UsedJustifySelf(aParent)._0;
2218 inline mozilla::StyleContentDistribution nsStylePosition::UsedContentAlignment(
2219 mozilla::LogicalAxis aAxis) const {
2220 return aAxis == mozilla::eLogicalAxisBlock ? mAlignContent : mJustifyContent;
2223 inline mozilla::StyleContentDistribution nsStylePosition::UsedTracksAlignment(
2224 mozilla::LogicalAxis aAxis, uint32_t aIndex) const {
2225 using T = mozilla::StyleAlignFlags;
2226 const auto& tracksAlignment =
2227 aAxis == mozilla::eLogicalAxisBlock ? mAlignTracks : mJustifyTracks;
2228 if (MOZ_LIKELY(tracksAlignment.IsEmpty())) {
2229 // An empty array encodes the initial value, 'normal', which behaves as
2230 // 'start' for Grid containers.
2231 return mozilla::StyleContentDistribution{T::START};
2234 // If there are fewer values than tracks, then the last value is used for all
2235 // the remaining tracks.
2236 const auto& ta = tracksAlignment.AsSpan();
2237 auto align = ta[std::min<size_t>(aIndex, ta.Length() - 1)];
2238 if (align.primary == T::NORMAL) {
2239 align = mozilla::StyleContentDistribution{T::START};
2241 return align;
2244 #endif // WritingModes_h_