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