Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef WritingModes_h_
7 #define WritingModes_h_
9 #include "nsRect.h"
10 #include "nsStyleStruct.h"
12 // If WRITING_MODE_VERTICAL_ENABLED is defined, we will attempt to support
13 // the vertical writing-mode values; if it is not defined, then
14 // WritingMode.IsVertical() will be hard-coded to return false, allowing
15 // many conditional branches to be optimized away while we're in the process
16 // of transitioning layout to use writing-mode and logical directions, but
17 // not yet ready to ship vertical support.
19 /* #define WRITING_MODE_VERTICAL_ENABLED 1 */
21 // It is the caller's responsibility to operate on logical-coordinate objects
22 // with matched writing modes. Failure to do so will be a runtime bug; the
23 // compiler can't catch it, but in debug mode, we'll throw an assertion.
24 // NOTE that in non-debug builds, a writing mode mismatch error will NOT be
25 // detected, yet the results will be nonsense (and may lead to further layout
26 // failures). Therefore, it is important to test (and fuzz-test) writing-mode
27 // support using debug builds.
29 // Methods in logical-coordinate classes that take another logical-coordinate
30 // object as a parameter should call CHECK_WRITING_MODE on it to verify that
31 // the writing modes match.
32 // (In some cases, there are internal (private) methods that don't do this;
33 // such methods should only be used by other methods that have already checked
34 // the writing modes.)
36 #define CHECK_WRITING_MODE(param) \
37 NS_ASSERTION(param == mWritingMode, "writing-mode mismatch")
39 /**
40 * mozilla::WritingMode is an immutable class representing a
41 * writing mode.
42 *
43 * It efficiently stores the writing mode and can rapidly compute
44 * interesting things about it for use in layout.
45 *
46 * Writing modes are computed from the CSS 'direction',
47 * 'writing-mode', and 'text-orientation' properties.
48 * See CSS3 Writing Modes for more information
49 * http://www.w3.org/TR/css3-writing-modes/
50 */
52 namespace mozilla {
54 class WritingMode {
55 public:
56 /**
57 * Absolute inline flow direction
58 */
59 enum InlineDir {
60 eInlineLTR = 0x00, // text flows horizontally left to right
61 eInlineRTL = 0x02, // text flows horizontally right to left
62 eInlineTTB = 0x01, // text flows vertically top to bottom
63 eInlineBTT = 0x03, // text flows vertically bottom to top
64 };
66 /**
67 * Absolute block flow direction
68 */
69 enum BlockDir {
70 eBlockTB = 0x00, // horizontal lines stack top to bottom
71 eBlockRL = 0x01, // vertical lines stack right to left
72 eBlockLR = 0x05, // vertical lines stack left to right
73 };
75 /**
76 * Line-relative (bidi-relative) inline flow direction
77 */
78 enum BidiDir {
79 eBidiLTR = 0x00, // inline flow matches bidi LTR text
80 eBidiRTL = 0x10, // inline flow matches bidi RTL text
81 };
83 /**
84 * Unknown writing mode (should never actually be stored or used anywhere).
85 */
86 enum {
87 eUnknownWritingMode = 0xff
88 };
90 /**
91 * Return the absolute inline flow direction as an InlineDir
92 */
93 InlineDir GetInlineDir() const { return InlineDir(mWritingMode & eInlineMask); }
95 /**
96 * Return the absolute block flow direction as a BlockDir
97 */
98 BlockDir GetBlockDir() const { return BlockDir(mWritingMode & eBlockMask); }
100 /**
101 * Return the line-relative inline flow direction as a BidiDir
102 */
103 BidiDir GetBidiDir() const { return BidiDir(mWritingMode & eBidiMask); }
105 /**
106 * Return true if LTR. (Convenience method)
107 */
108 bool IsBidiLTR() const { return eBidiLTR == (mWritingMode & eBidiMask); }
110 /**
111 * True if vertical-mode block direction is LR (convenience method).
112 */
113 bool IsVerticalLR() const { return eBlockLR == (mWritingMode & eBlockMask); }
115 /**
116 * True if vertical writing mode, i.e. when
117 * writing-mode: vertical-lr | vertical-rl.
118 */
119 #ifdef WRITING_MODE_VERTICAL_ENABLED
120 bool IsVertical() const { return !!(mWritingMode & eOrientationMask); }
121 #else
122 bool IsVertical() const { return false; }
123 #endif
125 /**
126 * True if line-over/line-under are inverted from block-start/block-end.
127 * This is true when
128 * - writing-mode is vertical-rl && text-orientation is sideways-left
129 * - writing-mode is vertical-lr && text-orientation is not sideways-left
130 */
131 #ifdef WRITING_MODE_VERTICAL_ENABLED
132 bool IsLineInverted() const { return !!(mWritingMode & eLineOrientMask); }
133 #else
134 bool IsLineInverted() const { return false; }
135 #endif
137 /**
138 * Block-axis flow-relative to line-relative factor.
139 * May be used as a multiplication factor for block-axis coordinates
140 * to convert between flow- and line-relative coordinate systems (e.g.
141 * positioning an over- or under-line decoration).
142 */
143 int FlowRelativeToLineRelativeFactor() const
144 {
145 return IsLineInverted() ? -1 : 1;
146 }
148 /**
149 * Default constructor gives us a horizontal, LTR writing mode.
150 * XXX We will probably eliminate this and require explicit initialization
151 * in all cases once transition is complete.
152 */
153 WritingMode()
154 : mWritingMode(0)
155 { }
157 /**
158 * Construct writing mode based on a style context
159 */
160 WritingMode(const nsStyleVisibility* aStyleVisibility)
161 {
162 NS_ASSERTION(aStyleVisibility, "we need an nsStyleVisibility here");
164 #ifdef WRITING_MODE_VERTICAL_ENABLED
165 switch (aStyleVisibility->mWritingMode) {
166 case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
167 mWritingMode = 0;
168 break;
170 case NS_STYLE_WRITING_MODE_VERTICAL_LR:
171 mWritingMode = eBlockFlowMask |
172 eLineOrientMask | //XXX needs update when text-orientation added
173 eOrientationMask;
174 break;
176 case NS_STYLE_WRITING_MODE_VERTICAL_RL:
177 mWritingMode = eOrientationMask;
178 break;
180 default:
181 NS_NOTREACHED("unknown writing mode!");
182 mWritingMode = 0;
183 break;
184 }
185 #else
186 mWritingMode = 0;
187 #endif
189 if (NS_STYLE_DIRECTION_RTL == aStyleVisibility->mDirection) {
190 mWritingMode |= eInlineFlowMask | //XXX needs update when text-orientation added
191 eBidiMask;
192 }
193 }
195 // For unicode-bidi: plaintext, reset the direction of the writing mode from
196 // the bidi paragraph level of the content
198 //XXX change uint8_t to UBiDiLevel after bug 924851
199 void SetDirectionFromBidiLevel(uint8_t level)
200 {
201 if (level & 1) {
202 // odd level, set RTL
203 mWritingMode |= eBidiMask;
204 } else {
205 // even level, set LTR
206 mWritingMode &= ~eBidiMask;
207 }
208 }
210 /**
211 * Compare two WritingModes for equality.
212 */
213 bool operator==(const WritingMode& aOther) const
214 {
215 return mWritingMode == aOther.mWritingMode;
216 }
218 private:
219 friend class LogicalPoint;
220 friend class LogicalSize;
221 friend class LogicalMargin;
222 friend class LogicalRect;
224 /**
225 * Return a WritingMode representing an unknown value.
226 */
227 static inline WritingMode Unknown()
228 {
229 return WritingMode(eUnknownWritingMode);
230 }
232 /**
233 * Constructing a WritingMode with an arbitrary value is a private operation
234 * currently only used by the Unknown() static method.
235 */
236 WritingMode(uint8_t aValue)
237 : mWritingMode(aValue)
238 { }
240 uint8_t mWritingMode;
242 enum Masks {
243 // Masks for our bits; true chosen as opposite of commonest case
244 eOrientationMask = 0x01, // true means vertical text
245 eInlineFlowMask = 0x02, // true means absolute RTL/BTT (against physical coords)
246 eBlockFlowMask = 0x04, // true means vertical-LR (or horizontal-BT if added)
247 eLineOrientMask = 0x08, // true means over != block-start
248 eBidiMask = 0x10, // true means line-relative RTL (bidi RTL)
249 // Note: We have one excess bit of info; WritingMode can pack into 4 bits.
250 // But since we have space, we're caching interesting things for fast access.
252 // Masks for output enums
253 eInlineMask = 0x03,
254 eBlockMask = 0x05
255 };
256 };
259 /**
260 * Logical-coordinate classes:
261 *
262 * There are three sets of coordinate space:
263 * - physical (top, left, bottom, right)
264 * relative to graphics coord system
265 * - flow-relative (block-start, inline-start, block-end, inline-end)
266 * relative to block/inline flow directions
267 * - line-relative (line-over, line-left, line-under, line-right)
268 * relative to glyph orientation / inline bidi directions
269 * See CSS3 Writing Modes for more information
270 * http://www.w3.org/TR/css3-writing-modes/#abstract-box
271 *
272 * For shorthand, B represents the block-axis
273 * I represents the inline-axis
274 *
275 * The flow-relative geometric classes store coords in flow-relative space.
276 * They use a private ns{Point,Size,Rect,Margin} member to store the actual
277 * coordinate values, but reinterpret them as logical instead of physical.
278 * This allows us to easily perform calculations in logical space (provided
279 * writing modes of the operands match), by simply mapping to nsPoint (etc)
280 * methods.
281 *
282 * Physical-coordinate accessors/setters are responsible to translate these
283 * internal logical values as necessary.
284 *
285 * In DEBUG builds, the logical types store their WritingMode and check
286 * that the same WritingMode is passed whenever callers ask them to do a
287 * writing-mode-dependent operation. Non-DEBUG builds do NOT check this,
288 * to avoid the overhead of storing WritingMode fields.
289 *
290 * Open question: do we need a different set optimized for line-relative
291 * math, for use in nsLineLayout and the like? Or is multiplying values
292 * by FlowRelativeToLineRelativeFactor() enough?
293 */
295 /**
296 * Flow-relative point
297 */
298 class LogicalPoint {
299 public:
300 LogicalPoint(WritingMode aWritingMode)
301 :
302 #ifdef DEBUG
303 mWritingMode(aWritingMode),
304 #endif
305 mPoint(0, 0)
306 { }
308 // Construct from a writing mode and individual coordinates (which MUST be
309 // values in that writing mode, NOT physical coordinates!)
310 LogicalPoint(WritingMode aWritingMode, nscoord aI, nscoord aB)
311 :
312 #ifdef DEBUG
313 mWritingMode(aWritingMode),
314 #endif
315 mPoint(aI, aB)
316 { }
318 // Construct from a writing mode and a physical point, within a given
319 // containing rectangle's width (defining the conversion between LTR
320 // and RTL coordinates).
321 LogicalPoint(WritingMode aWritingMode,
322 const nsPoint& aPoint,
323 nscoord aContainerWidth)
324 #ifdef DEBUG
325 : mWritingMode(aWritingMode)
326 #endif
327 {
328 if (aWritingMode.IsVertical()) {
329 I() = aPoint.y;
330 B() = aWritingMode.IsVerticalLR() ? aPoint.x : aContainerWidth - aPoint.x;
331 } else {
332 I() = aWritingMode.IsBidiLTR() ? aPoint.x : aContainerWidth - aPoint.x;
333 B() = aPoint.y;
334 }
335 }
337 /**
338 * Read-only (const) access to the coordinates, in both logical
339 * and physical terms.
340 */
341 nscoord I(WritingMode aWritingMode) const // inline-axis
342 {
343 CHECK_WRITING_MODE(aWritingMode);
344 return mPoint.x;
345 }
346 nscoord B(WritingMode aWritingMode) const // block-axis
347 {
348 CHECK_WRITING_MODE(aWritingMode);
349 return mPoint.y;
350 }
352 nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
353 {
354 CHECK_WRITING_MODE(aWritingMode);
355 if (aWritingMode.IsVertical()) {
356 return aWritingMode.IsVerticalLR() ? B() : aContainerWidth - B();
357 } else {
358 return aWritingMode.IsBidiLTR() ? I() : aContainerWidth - I();
359 }
360 }
361 nscoord Y(WritingMode aWritingMode) const
362 {
363 CHECK_WRITING_MODE(aWritingMode);
364 return aWritingMode.IsVertical() ? I() : B();
365 }
367 /**
368 * These non-const accessors return a reference (lvalue) that can be
369 * assigned to by callers.
370 */
371 nscoord& I(WritingMode aWritingMode) // inline-axis
372 {
373 CHECK_WRITING_MODE(aWritingMode);
374 return mPoint.x;
375 }
376 nscoord& B(WritingMode aWritingMode) // block-axis
377 {
378 CHECK_WRITING_MODE(aWritingMode);
379 return mPoint.y;
380 }
382 /**
383 * Setters for the physical coordinates.
384 */
385 void SetX(WritingMode aWritingMode, nscoord aX, nscoord aContainerWidth)
386 {
387 CHECK_WRITING_MODE(aWritingMode);
388 if (aWritingMode.IsVertical()) {
389 B() = aWritingMode.IsVerticalLR() ? aX : aContainerWidth - aX;
390 } else {
391 I() = aWritingMode.IsBidiLTR() ? aX : aContainerWidth - aX;
392 }
393 }
394 void SetY(WritingMode aWritingMode, nscoord aY)
395 {
396 CHECK_WRITING_MODE(aWritingMode);
397 if (aWritingMode.IsVertical()) {
398 B() = aY;
399 } else {
400 I() = aY;
401 }
402 }
404 /**
405 * Return a physical point corresponding to our logical coordinates,
406 * converted according to our writing mode.
407 */
408 nsPoint GetPhysicalPoint(WritingMode aWritingMode,
409 nscoord aContainerWidth) const
410 {
411 CHECK_WRITING_MODE(aWritingMode);
412 if (aWritingMode.IsVertical()) {
413 return nsPoint(aWritingMode.IsVerticalLR() ? B() : aContainerWidth - B(),
414 I());
415 } else {
416 return nsPoint(aWritingMode.IsBidiLTR() ? I() : aContainerWidth - I(),
417 B());
418 }
419 }
421 /**
422 * Return the equivalent point in a different writing mode.
423 */
424 LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode,
425 nscoord aContainerWidth) const
426 {
427 CHECK_WRITING_MODE(aFromMode);
428 return aToMode == aFromMode ?
429 *this : LogicalPoint(aToMode,
430 GetPhysicalPoint(aFromMode, aContainerWidth),
431 aContainerWidth);
432 }
434 LogicalPoint operator+(const LogicalPoint& aOther) const
435 {
436 CHECK_WRITING_MODE(aOther.GetWritingMode());
437 // In non-debug builds, LogicalPoint does not store the WritingMode,
438 // so the first parameter here (which will always be eUnknownWritingMode)
439 // is ignored.
440 return LogicalPoint(GetWritingMode(),
441 mPoint.x + aOther.mPoint.x,
442 mPoint.y + aOther.mPoint.y);
443 }
445 private:
446 friend class LogicalRect;
448 /**
449 * NOTE that in non-DEBUG builds, GetWritingMode() always returns
450 * eUnknownWritingMode, as the current mode is not stored in the logical-
451 * geometry classes. Therefore, this method is private; it is used ONLY
452 * by the DEBUG-mode checking macros in this class and its friends;
453 * other code is not allowed to ask a logical point for its writing mode,
454 * as this info will simply not be available in non-DEBUG builds.
455 *
456 * Also, in non-DEBUG builds, CHECK_WRITING_MODE does nothing, and the
457 * WritingMode parameter to logical methods will generally be optimized
458 * away altogether.
459 */
460 #ifdef DEBUG
461 WritingMode GetWritingMode() const { return mWritingMode; }
462 #else
463 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
464 #endif
466 // We don't allow construction of a LogicalPoint with no writing mode.
467 LogicalPoint() MOZ_DELETE;
469 // Accessors that don't take or check a WritingMode value.
470 // These are for internal use only; they are called by methods that have
471 // themselves already checked the WritingMode passed by the caller.
472 nscoord I() const // inline-axis
473 {
474 return mPoint.x;
475 }
476 nscoord B() const // block-axis
477 {
478 return mPoint.y;
479 }
481 nscoord& I() // inline-axis
482 {
483 return mPoint.x;
484 }
485 nscoord& B() // block-axis
486 {
487 return mPoint.y;
488 }
490 WritingMode mWritingMode;
492 // We use an nsPoint to hold the coordinates, but reinterpret its .x and .y
493 // fields as the inline and block directions. Hence, this is not exposed
494 // directly, but only through accessors that will map them according to the
495 // writing mode.
496 nsPoint mPoint;
497 };
499 /**
500 * Flow-relative size
501 */
502 class LogicalSize {
503 public:
504 LogicalSize(WritingMode aWritingMode)
505 :
506 #ifdef DEBUG
507 mWritingMode(aWritingMode),
508 #endif
509 mSize(0, 0)
510 { }
512 LogicalSize(WritingMode aWritingMode, nscoord aISize, nscoord aBSize)
513 :
514 #ifdef DEBUG
515 mWritingMode(aWritingMode),
516 #endif
517 mSize(aISize, aBSize)
518 { }
520 LogicalSize(WritingMode aWritingMode, const nsSize& aPhysicalSize)
521 #ifdef DEBUG
522 : mWritingMode(aWritingMode)
523 #endif
524 {
525 if (aWritingMode.IsVertical()) {
526 ISize() = aPhysicalSize.height;
527 BSize() = aPhysicalSize.width;
528 } else {
529 ISize() = aPhysicalSize.width;
530 BSize() = aPhysicalSize.height;
531 }
532 }
534 /**
535 * Dimensions in logical and physical terms
536 */
537 nscoord ISize(WritingMode aWritingMode) const // inline-size
538 {
539 CHECK_WRITING_MODE(aWritingMode);
540 return mSize.width;
541 }
542 nscoord BSize(WritingMode aWritingMode) const // block-size
543 {
544 CHECK_WRITING_MODE(aWritingMode);
545 return mSize.height;
546 }
548 nscoord Width(WritingMode aWritingMode) const
549 {
550 CHECK_WRITING_MODE(aWritingMode);
551 return aWritingMode.IsVertical() ? BSize() : ISize();
552 }
553 nscoord Height(WritingMode aWritingMode) const
554 {
555 CHECK_WRITING_MODE(aWritingMode);
556 return aWritingMode.IsVertical() ? ISize() : BSize();
557 }
559 /**
560 * Writable references to the logical dimensions
561 */
562 nscoord& ISize(WritingMode aWritingMode) // inline-size
563 {
564 CHECK_WRITING_MODE(aWritingMode);
565 return mSize.width;
566 }
567 nscoord& BSize(WritingMode aWritingMode) // block-size
568 {
569 CHECK_WRITING_MODE(aWritingMode);
570 return mSize.height;
571 }
573 /**
574 * Setters for the physical dimensions
575 */
576 void SetWidth(WritingMode aWritingMode, nscoord aWidth)
577 {
578 CHECK_WRITING_MODE(aWritingMode);
579 if (aWritingMode.IsVertical()) {
580 BSize() = aWidth;
581 } else {
582 ISize() = aWidth;
583 }
584 }
585 void SetHeight(WritingMode aWritingMode, nscoord aHeight)
586 {
587 CHECK_WRITING_MODE(aWritingMode);
588 if (aWritingMode.IsVertical()) {
589 ISize() = aHeight;
590 } else {
591 BSize() = aHeight;
592 }
593 }
595 /**
596 * Return an nsSize containing our physical dimensions
597 */
598 nsSize GetPhysicalSize(WritingMode aWritingMode) const
599 {
600 CHECK_WRITING_MODE(aWritingMode);
601 return aWritingMode.IsVertical() ?
602 nsSize(BSize(), ISize()) : nsSize(ISize(), BSize());
603 }
605 /**
606 * Return a LogicalSize representing this size in a different writing mode
607 */
608 LogicalSize ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
609 {
610 CHECK_WRITING_MODE(aFromMode);
611 return aToMode == aFromMode ?
612 *this : LogicalSize(aToMode, GetPhysicalSize(aFromMode));
613 }
615 private:
616 friend class LogicalRect;
618 LogicalSize() MOZ_DELETE;
620 #ifdef DEBUG
621 WritingMode GetWritingMode() const { return mWritingMode; }
622 #else
623 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
624 #endif
626 nscoord ISize() const // inline-size
627 {
628 return mSize.width;
629 }
630 nscoord BSize() const // block-size
631 {
632 return mSize.height;
633 }
635 nscoord& ISize() // inline-size
636 {
637 return mSize.width;
638 }
639 nscoord& BSize() // block-size
640 {
641 return mSize.height;
642 }
644 WritingMode mWritingMode;
645 nsSize mSize;
646 };
648 /**
649 * Flow-relative margin
650 */
651 class LogicalMargin {
652 public:
653 LogicalMargin(WritingMode aWritingMode)
654 :
655 #ifdef DEBUG
656 mWritingMode(aWritingMode),
657 #endif
658 mMargin(0, 0, 0, 0)
659 { }
661 LogicalMargin(WritingMode aWritingMode,
662 nscoord aBStart, nscoord aIEnd,
663 nscoord aBEnd, nscoord aIStart)
664 :
665 #ifdef DEBUG
666 mWritingMode(aWritingMode),
667 #endif
668 mMargin(aBStart, aIEnd, aBEnd, aIStart)
669 { }
671 LogicalMargin(WritingMode aWritingMode, const nsMargin& aPhysicalMargin)
672 #ifdef DEBUG
673 : mWritingMode(aWritingMode)
674 #endif
675 {
676 if (aWritingMode.IsVertical()) {
677 if (aWritingMode.IsVerticalLR()) {
678 mMargin.top = aPhysicalMargin.left;
679 mMargin.bottom = aPhysicalMargin.right;
680 } else {
681 mMargin.top = aPhysicalMargin.right;
682 mMargin.bottom = aPhysicalMargin.left;
683 }
684 if (aWritingMode.IsBidiLTR()) {
685 mMargin.left = aPhysicalMargin.top;
686 mMargin.right = aPhysicalMargin.bottom;
687 } else {
688 mMargin.left = aPhysicalMargin.bottom;
689 mMargin.right = aPhysicalMargin.top;
690 }
691 } else {
692 mMargin.top = aPhysicalMargin.top;
693 mMargin.bottom = aPhysicalMargin.bottom;
694 if (aWritingMode.IsBidiLTR()) {
695 mMargin.left = aPhysicalMargin.left;
696 mMargin.right = aPhysicalMargin.right;
697 } else {
698 mMargin.left = aPhysicalMargin.right;
699 mMargin.right = aPhysicalMargin.left;
700 }
701 }
702 }
704 nscoord IStart(WritingMode aWritingMode) const // inline-start margin
705 {
706 CHECK_WRITING_MODE(aWritingMode);
707 return mMargin.left;
708 }
709 nscoord IEnd(WritingMode aWritingMode) const // inline-end margin
710 {
711 CHECK_WRITING_MODE(aWritingMode);
712 return mMargin.right;
713 }
714 nscoord BStart(WritingMode aWritingMode) const // block-start margin
715 {
716 CHECK_WRITING_MODE(aWritingMode);
717 return mMargin.top;
718 }
719 nscoord BEnd(WritingMode aWritingMode) const // block-end margin
720 {
721 CHECK_WRITING_MODE(aWritingMode);
722 return mMargin.bottom;
723 }
725 nscoord& IStart(WritingMode aWritingMode) // inline-start margin
726 {
727 CHECK_WRITING_MODE(aWritingMode);
728 return mMargin.left;
729 }
730 nscoord& IEnd(WritingMode aWritingMode) // inline-end margin
731 {
732 CHECK_WRITING_MODE(aWritingMode);
733 return mMargin.right;
734 }
735 nscoord& BStart(WritingMode aWritingMode) // block-start margin
736 {
737 CHECK_WRITING_MODE(aWritingMode);
738 return mMargin.top;
739 }
740 nscoord& BEnd(WritingMode aWritingMode) // block-end margin
741 {
742 CHECK_WRITING_MODE(aWritingMode);
743 return mMargin.bottom;
744 }
746 nscoord IStartEnd(WritingMode aWritingMode) const // inline margins
747 {
748 CHECK_WRITING_MODE(aWritingMode);
749 return mMargin.LeftRight();
750 }
751 nscoord BStartEnd(WritingMode aWritingMode) const // block margins
752 {
753 CHECK_WRITING_MODE(aWritingMode);
754 return mMargin.TopBottom();
755 }
757 /**
758 * Accessors for physical margins, using our writing mode to convert from
759 * logical values.
760 */
761 nscoord Top(WritingMode aWritingMode) const
762 {
763 CHECK_WRITING_MODE(aWritingMode);
764 return aWritingMode.IsVertical() ?
765 (aWritingMode.IsBidiLTR() ? IStart() : IEnd()) : BStart();
766 }
768 nscoord Bottom(WritingMode aWritingMode) const
769 {
770 CHECK_WRITING_MODE(aWritingMode);
771 return aWritingMode.IsVertical() ?
772 (aWritingMode.IsBidiLTR() ? IEnd() : IStart()) : BEnd();
773 }
775 nscoord Left(WritingMode aWritingMode) const
776 {
777 CHECK_WRITING_MODE(aWritingMode);
778 return aWritingMode.IsVertical() ?
779 (aWritingMode.IsVerticalLR() ? BStart() : BEnd()) :
780 (aWritingMode.IsBidiLTR() ? IStart() : IEnd());
781 }
783 nscoord Right(WritingMode aWritingMode) const
784 {
785 CHECK_WRITING_MODE(aWritingMode);
786 return aWritingMode.IsVertical() ?
787 (aWritingMode.IsVerticalLR() ? BEnd() : BStart()) :
788 (aWritingMode.IsBidiLTR() ? IEnd() : IStart());
789 }
791 nscoord LeftRight(WritingMode aWritingMode) const
792 {
793 CHECK_WRITING_MODE(aWritingMode);
794 return aWritingMode.IsVertical() ? BStartEnd() : IStartEnd();
795 }
797 nscoord TopBottom(WritingMode aWritingMode) const
798 {
799 CHECK_WRITING_MODE(aWritingMode);
800 return aWritingMode.IsVertical() ? IStartEnd() : BStartEnd();
801 }
803 void SizeTo(WritingMode aWritingMode,
804 nscoord aBStart, nscoord aIEnd, nscoord aBEnd, nscoord aIStart)
805 {
806 CHECK_WRITING_MODE(aWritingMode);
807 mMargin.SizeTo(aBStart, aIEnd, aBEnd, aIStart);
808 }
810 /**
811 * Return an nsMargin containing our physical coordinates
812 */
813 nsMargin GetPhysicalMargin(WritingMode aWritingMode) const
814 {
815 CHECK_WRITING_MODE(aWritingMode);
816 return aWritingMode.IsVertical() ?
817 (aWritingMode.IsVerticalLR() ?
818 nsMargin(IStart(), BEnd(), IEnd(), BStart()) :
819 nsMargin(IStart(), BStart(), IEnd(), BEnd())) :
820 (aWritingMode.IsBidiLTR() ?
821 nsMargin(BStart(), IEnd(), BEnd(), IStart()) :
822 nsMargin(BStart(), IStart(), BEnd(), IEnd()));
823 }
825 /**
826 * Return a LogicalMargin representing this margin in a different
827 * writing mode
828 */
829 LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
830 {
831 CHECK_WRITING_MODE(aFromMode);
832 return aToMode == aFromMode ?
833 *this : LogicalMargin(aToMode, GetPhysicalMargin(aFromMode));
834 }
836 bool IsEmpty() const
837 {
838 return (mMargin.left == 0 && mMargin.top == 0 &&
839 mMargin.right == 0 && mMargin.bottom == 0);
840 }
842 LogicalMargin operator+(const LogicalMargin& aMargin) {
843 CHECK_WRITING_MODE(aMargin.GetWritingMode());
844 return LogicalMargin(GetWritingMode(),
845 BStart() + aMargin.BStart(),
846 IEnd() + aMargin.IEnd(),
847 BEnd() + aMargin.BEnd(),
848 IStart() + aMargin.IStart());
849 }
851 LogicalMargin operator-(const LogicalMargin& aMargin) {
852 CHECK_WRITING_MODE(aMargin.GetWritingMode());
853 return LogicalMargin(GetWritingMode(),
854 BStart() - aMargin.BStart(),
855 IEnd() - aMargin.IEnd(),
856 BEnd() - aMargin.BEnd(),
857 IStart() - aMargin.IStart());
858 }
860 private:
861 friend class LogicalRect;
863 LogicalMargin() MOZ_DELETE;
865 #ifdef DEBUG
866 WritingMode GetWritingMode() const { return mWritingMode; }
867 #else
868 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
869 #endif
871 nscoord IStart() const // inline-start margin
872 {
873 return mMargin.left;
874 }
875 nscoord IEnd() const // inline-end margin
876 {
877 return mMargin.right;
878 }
879 nscoord BStart() const // block-start margin
880 {
881 return mMargin.top;
882 }
883 nscoord BEnd() const // block-end margin
884 {
885 return mMargin.bottom;
886 }
888 nscoord& IStart() // inline-start margin
889 {
890 return mMargin.left;
891 }
892 nscoord& IEnd() // inline-end margin
893 {
894 return mMargin.right;
895 }
896 nscoord& BStart() // block-start margin
897 {
898 return mMargin.top;
899 }
900 nscoord& BEnd() // block-end margin
901 {
902 return mMargin.bottom;
903 }
905 nscoord IStartEnd() const // inline margins
906 {
907 return mMargin.LeftRight();
908 }
909 nscoord BStartEnd() const // block margins
910 {
911 return mMargin.TopBottom();
912 }
914 WritingMode mWritingMode;
915 nsMargin mMargin;
916 };
918 /**
919 * Flow-relative rectangle
920 */
921 class LogicalRect {
922 public:
923 LogicalRect(WritingMode aWritingMode)
924 :
925 #ifdef DEBUG
926 mWritingMode(aWritingMode),
927 #endif
928 mRect(0, 0, 0, 0)
929 { }
931 LogicalRect(WritingMode aWritingMode,
932 nscoord aIStart, nscoord aBStart,
933 nscoord aISize, nscoord aBSize)
934 :
935 #ifdef DEBUG
936 mWritingMode(aWritingMode),
937 #endif
938 mRect(aIStart, aBStart, aISize, aBSize)
939 { }
941 LogicalRect(WritingMode aWritingMode,
942 const LogicalPoint& aOrigin,
943 const LogicalSize& aSize)
944 :
945 #ifdef DEBUG
946 mWritingMode(aWritingMode),
947 #endif
948 mRect(aOrigin.mPoint, aSize.mSize)
949 {
950 CHECK_WRITING_MODE(aOrigin.GetWritingMode());
951 CHECK_WRITING_MODE(aSize.GetWritingMode());
952 }
954 LogicalRect(WritingMode aWritingMode,
955 const nsRect& aRect,
956 nscoord aContainerWidth)
957 #ifdef DEBUG
958 : mWritingMode(aWritingMode)
959 #endif
960 {
961 if (aWritingMode.IsVertical()) {
962 if (aWritingMode.IsVerticalLR()) {
963 mRect.y = aRect.x;
964 } else {
965 mRect.y = aContainerWidth - aRect.XMost();
966 }
967 mRect.height = aRect.width;
968 mRect.x = aRect.y;
969 mRect.width = aRect.height;
970 } else {
971 if (aWritingMode.IsBidiLTR()) {
972 mRect.x = aRect.x;
973 } else {
974 mRect.x = aContainerWidth - aRect.XMost();
975 }
976 mRect.width = aRect.width;
977 mRect.y = aRect.y;
978 mRect.height = aRect.height;
979 }
980 }
982 /**
983 * Inline- and block-dimension geometry.
984 */
985 nscoord IStart(WritingMode aWritingMode) const // inline-start edge
986 {
987 CHECK_WRITING_MODE(aWritingMode);
988 return mRect.X();
989 }
990 nscoord IEnd(WritingMode aWritingMode) const // inline-end edge
991 {
992 CHECK_WRITING_MODE(aWritingMode);
993 return mRect.XMost();
994 }
995 nscoord ISize(WritingMode aWritingMode) const // inline-size
996 {
997 CHECK_WRITING_MODE(aWritingMode);
998 return mRect.Width();
999 }
1001 nscoord BStart(WritingMode aWritingMode) const // block-start edge
1002 {
1003 CHECK_WRITING_MODE(aWritingMode);
1004 return mRect.Y();
1005 }
1006 nscoord BEnd(WritingMode aWritingMode) const // block-end edge
1007 {
1008 CHECK_WRITING_MODE(aWritingMode);
1009 return mRect.YMost();
1010 }
1011 nscoord BSize(WritingMode aWritingMode) const // block-size
1012 {
1013 CHECK_WRITING_MODE(aWritingMode);
1014 return mRect.Height();
1015 }
1017 /**
1018 * Writable (reference) accessors are only available for the basic logical
1019 * fields (Start and Size), not derivatives like End.
1020 */
1021 nscoord& IStart(WritingMode aWritingMode) // inline-start edge
1022 {
1023 CHECK_WRITING_MODE(aWritingMode);
1024 return mRect.x;
1025 }
1026 nscoord& ISize(WritingMode aWritingMode) // inline-size
1027 {
1028 CHECK_WRITING_MODE(aWritingMode);
1029 return mRect.width;
1030 }
1031 nscoord& BStart(WritingMode aWritingMode) // block-start edge
1032 {
1033 CHECK_WRITING_MODE(aWritingMode);
1034 return mRect.y;
1035 }
1036 nscoord& BSize(WritingMode aWritingMode) // block-size
1037 {
1038 CHECK_WRITING_MODE(aWritingMode);
1039 return mRect.height;
1040 }
1042 /**
1043 * Physical coordinates of the rect.
1044 */
1045 nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const
1046 {
1047 CHECK_WRITING_MODE(aWritingMode);
1048 if (aWritingMode.IsVertical()) {
1049 return aWritingMode.IsVerticalLR() ?
1050 mRect.Y() : aContainerWidth - mRect.YMost();
1051 } else {
1052 return aWritingMode.IsBidiLTR() ?
1053 mRect.X() : aContainerWidth - mRect.XMost();
1054 }
1055 }
1057 void SetX(WritingMode aWritingMode, nscoord aX, nscoord aContainerWidth)
1058 {
1059 CHECK_WRITING_MODE(aWritingMode);
1060 if (aWritingMode.IsVertical()) {
1061 if (aWritingMode.IsVerticalLR()) {
1062 BStart() = aX;
1063 } else {
1064 BStart() = aContainerWidth - aX - BSize();
1065 }
1066 } else {
1067 if (aWritingMode.IsBidiLTR()) {
1068 IStart() = aX;
1069 } else {
1070 IStart() = aContainerWidth - aX - ISize();
1071 }
1072 }
1073 }
1075 nscoord Y(WritingMode aWritingMode) const
1076 {
1077 CHECK_WRITING_MODE(aWritingMode);
1078 return aWritingMode.IsVertical() ? mRect.X() : mRect.Y();
1079 }
1081 void SetY(WritingMode aWritingMode, nscoord aY)
1082 {
1083 CHECK_WRITING_MODE(aWritingMode);
1084 if (aWritingMode.IsVertical()) {
1085 IStart() = aY;
1086 } else {
1087 BStart() = aY;
1088 }
1089 }
1091 nscoord Width(WritingMode aWritingMode) const
1092 {
1093 CHECK_WRITING_MODE(aWritingMode);
1094 return aWritingMode.IsVertical() ? mRect.Height() : mRect.Width();
1095 }
1097 // When setting the Width of a rect, we keep its physical X-coord fixed
1098 // and modify XMax. This means that in the RTL case, we'll be moving
1099 // the IStart, so that IEnd remains constant.
1100 void SetWidth(WritingMode aWritingMode, nscoord aWidth)
1101 {
1102 CHECK_WRITING_MODE(aWritingMode);
1103 if (aWritingMode.IsVertical()) {
1104 if (!aWritingMode.IsVerticalLR()) {
1105 BStart() = BStart() + BSize() - aWidth;
1106 }
1107 BSize() = aWidth;
1108 } else {
1109 if (!aWritingMode.IsBidiLTR()) {
1110 IStart() = IStart() + ISize() - aWidth;
1111 }
1112 ISize() = aWidth;
1113 }
1114 }
1116 nscoord Height(WritingMode aWritingMode) const
1117 {
1118 CHECK_WRITING_MODE(aWritingMode);
1119 return aWritingMode.IsVertical() ? mRect.Width() : mRect.Height();
1120 }
1122 void SetHeight(WritingMode aWritingMode, nscoord aHeight)
1123 {
1124 CHECK_WRITING_MODE(aWritingMode);
1125 if (aWritingMode.IsVertical()) {
1126 ISize() = aHeight;
1127 } else {
1128 BSize() = aHeight;
1129 }
1130 }
1132 nscoord XMost(WritingMode aWritingMode, nscoord aContainerWidth) const
1133 {
1134 CHECK_WRITING_MODE(aWritingMode);
1135 if (aWritingMode.IsVertical()) {
1136 return aWritingMode.IsVerticalLR() ?
1137 mRect.YMost() : aContainerWidth - mRect.Y();
1138 } else {
1139 return aWritingMode.IsBidiLTR() ?
1140 mRect.XMost() : aContainerWidth - mRect.X();
1141 }
1142 }
1144 nscoord YMost(WritingMode aWritingMode) const
1145 {
1146 CHECK_WRITING_MODE(aWritingMode);
1147 return aWritingMode.IsVertical() ? mRect.XMost() : mRect.YMost();
1148 }
1150 bool IsEmpty() const
1151 {
1152 return (mRect.x == 0 && mRect.y == 0 &&
1153 mRect.width == 0 && mRect.height == 0);
1154 }
1156 bool IsZeroSize() const
1157 {
1158 return (mRect.width == 0 && mRect.height == 0);
1159 }
1161 /* XXX are these correct?
1162 nscoord ILeft(WritingMode aWritingMode) const
1163 {
1164 CHECK_WRITING_MODE(aWritingMode);
1165 return aWritingMode.IsBidiLTR() ? IStart() : IEnd();
1166 }
1167 nscoord IRight(WritingMode aWritingMode) const
1168 {
1169 CHECK_WRITING_MODE(aWritingMode);
1170 return aWritingMode.IsBidiLTR() ? IEnd() : IStart();
1171 }
1172 */
1174 LogicalPoint Origin(WritingMode aWritingMode) const
1175 {
1176 CHECK_WRITING_MODE(aWritingMode);
1177 return LogicalPoint(aWritingMode, IStart(), BStart());
1178 }
1179 LogicalSize Size(WritingMode aWritingMode) const
1180 {
1181 CHECK_WRITING_MODE(aWritingMode);
1182 return LogicalSize(aWritingMode, ISize(), BSize());
1183 }
1185 LogicalRect operator+(const LogicalPoint& aPoint) const
1186 {
1187 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1188 return LogicalRect(GetWritingMode(),
1189 IStart() + aPoint.I(), BStart() + aPoint.B(),
1190 ISize(), BSize());
1191 }
1193 LogicalRect& operator+=(const LogicalPoint& aPoint)
1194 {
1195 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1196 mRect += aPoint.mPoint;
1197 return *this;
1198 }
1200 LogicalRect operator-(const LogicalPoint& aPoint) const
1201 {
1202 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1203 return LogicalRect(GetWritingMode(),
1204 IStart() - aPoint.I(), BStart() - aPoint.B(),
1205 ISize(), BSize());
1206 }
1208 LogicalRect& operator-=(const LogicalPoint& aPoint)
1209 {
1210 CHECK_WRITING_MODE(aPoint.GetWritingMode());
1211 mRect -= aPoint.mPoint;
1212 return *this;
1213 }
1215 void MoveBy(WritingMode aWritingMode, const LogicalPoint& aDelta)
1216 {
1217 CHECK_WRITING_MODE(aWritingMode);
1218 CHECK_WRITING_MODE(aDelta.GetWritingMode());
1219 IStart() += aDelta.I();
1220 BStart() += aDelta.B();
1221 }
1223 void Inflate(nscoord aD) { mRect.Inflate(aD); }
1224 void Inflate(nscoord aDI, nscoord aDB) { mRect.Inflate(aDI, aDB); }
1225 void Inflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
1226 {
1227 CHECK_WRITING_MODE(aWritingMode);
1228 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1229 mRect.Inflate(aMargin.mMargin);
1230 }
1232 void Deflate(nscoord aD) { mRect.Deflate(aD); }
1233 void Deflate(nscoord aDI, nscoord aDB) { mRect.Deflate(aDI, aDB); }
1234 void Deflate(WritingMode aWritingMode, const LogicalMargin& aMargin)
1235 {
1236 CHECK_WRITING_MODE(aWritingMode);
1237 CHECK_WRITING_MODE(aMargin.GetWritingMode());
1238 mRect.Deflate(aMargin.mMargin);
1239 }
1241 /**
1242 * Return an nsRect containing our physical coordinates within the given
1243 * container width
1244 */
1245 nsRect GetPhysicalRect(WritingMode aWritingMode,
1246 nscoord aContainerWidth) const
1247 {
1248 CHECK_WRITING_MODE(aWritingMode);
1249 if (aWritingMode.IsVertical()) {
1250 return nsRect(aWritingMode.IsVerticalLR() ?
1251 BStart() : aContainerWidth - BEnd(),
1252 IStart(), BSize(), ISize());
1253 } else {
1254 return nsRect(aWritingMode.IsBidiLTR() ?
1255 IStart() : aContainerWidth - IEnd(),
1256 BStart(), ISize(), BSize());
1257 }
1258 }
1260 #if 0 // XXX this would require aContainerWidth as well
1261 /**
1262 * Return a LogicalRect representing this rect in a different writing mode
1263 */
1264 LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode) const
1265 {
1266 CHECK_WRITING_MODE(aFromMode);
1267 return aToMode == aFromMode ?
1268 *this : LogicalRect(aToMode, GetPhysicalRect(aFromMode));
1269 }
1270 #endif
1272 private:
1273 LogicalRect() MOZ_DELETE;
1275 #ifdef DEBUG
1276 WritingMode GetWritingMode() const { return mWritingMode; }
1277 #else
1278 WritingMode GetWritingMode() const { return WritingMode::Unknown(); }
1279 #endif
1281 nscoord IStart() const // inline-start edge
1282 {
1283 return mRect.X();
1284 }
1285 nscoord IEnd() const // inline-end edge
1286 {
1287 return mRect.XMost();
1288 }
1289 nscoord ISize() const // inline-size
1290 {
1291 return mRect.Width();
1292 }
1294 nscoord BStart() const // block-start edge
1295 {
1296 return mRect.Y();
1297 }
1298 nscoord BEnd() const // block-end edge
1299 {
1300 return mRect.YMost();
1301 }
1302 nscoord BSize() const // block-size
1303 {
1304 return mRect.Height();
1305 }
1307 nscoord& IStart() // inline-start edge
1308 {
1309 return mRect.x;
1310 }
1311 nscoord& ISize() // inline-size
1312 {
1313 return mRect.width;
1314 }
1315 nscoord& BStart() // block-start edge
1316 {
1317 return mRect.y;
1318 }
1319 nscoord& BSize() // block-size
1320 {
1321 return mRect.height;
1322 }
1324 WritingMode mWritingMode;
1325 nsRect mRect;
1326 };
1328 } // namespace mozilla
1330 #endif // WritingModes_h_