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