Backed out 5 changesets (bug 1731541) for causing multiple wpt failures. CLOSED TREE
[gecko.git] / servo / components / style / logical_geometry.rs
blobf0842a11b2db24c5607848a487085376fdf2fb9e
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 //! Geometry in flow-relative space.
7 use crate::properties::style_structs;
8 use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D};
9 use euclid::num::Zero;
10 use std::cmp::{max, min};
11 use std::fmt::{self, Debug, Error, Formatter};
12 use std::ops::{Add, Sub};
13 use unicode_bidi as bidi;
15 pub enum BlockFlowDirection {
16     TopToBottom,
17     RightToLeft,
18     LeftToRight,
21 pub enum InlineBaseDirection {
22     LeftToRight,
23     RightToLeft,
26 // TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt()
27 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Serialize)]
28 #[repr(C)]
29 pub struct WritingMode(u8);
30 bitflags!(
31     impl WritingMode: u8 {
32         /// A vertical writing mode; writing-mode is vertical-rl,
33         /// vertical-lr, sideways-lr, or sideways-rl.
34         const VERTICAL = 1 << 0;
35         /// The inline flow direction is reversed against the physical
36         /// direction (i.e. right-to-left or bottom-to-top); writing-mode is
37         /// sideways-lr or direction is rtl (but not both).
38         ///
39         /// (This bit can be derived from the others, but we store it for
40         /// convenience.)
41         const INLINE_REVERSED = 1 << 1;
42         /// A vertical writing mode whose block progression direction is left-
43         /// to-right; writing-mode is vertical-lr or sideways-lr.
44         ///
45         /// Never set without VERTICAL.
46         const VERTICAL_LR = 1 << 2;
47         /// The line-over/line-under sides are inverted with respect to the
48         /// block-start/block-end edge; writing-mode is vertical-lr.
49         ///
50         /// Never set without VERTICAL and VERTICAL_LR.
51         const LINE_INVERTED = 1 << 3;
52         /// direction is rtl.
53         const RTL = 1 << 4;
54         /// All text within a vertical writing mode is displayed sideways
55         /// and runs top-to-bottom or bottom-to-top; set in these cases:
56         ///
57         /// * writing-mode: sideways-rl;
58         /// * writing-mode: sideways-lr;
59         ///
60         /// Never set without VERTICAL.
61         const VERTICAL_SIDEWAYS = 1 << 5;
62         /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation;
63         /// set in these cases:
64         ///
65         /// * writing-mode: vertical-rl; text-orientation: sideways;
66         /// * writing-mode: vertical-lr; text-orientation: sideways;
67         ///
68         /// Never set without VERTICAL.
69         const TEXT_SIDEWAYS = 1 << 6;
70         /// Horizontal text within a vertical writing mode is displayed with each
71         /// glyph upright; set in these cases:
72         ///
73         /// * writing-mode: vertical-rl; text-orientation: upright;
74         /// * writing-mode: vertical-lr: text-orientation: upright;
75         ///
76         /// Never set without VERTICAL.
77         const UPRIGHT = 1 << 7;
78     }
81 impl WritingMode {
82     /// Return a WritingMode bitflags from the relevant CSS properties.
83     pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self {
84         use crate::properties::longhands::direction::computed_value::T as Direction;
85         use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
87         let mut flags = WritingMode::empty();
89         let direction = inheritedbox_style.clone_direction();
90         let writing_mode = inheritedbox_style.clone_writing_mode();
92         match direction {
93             Direction::Ltr => {},
94             Direction::Rtl => {
95                 flags.insert(WritingMode::RTL);
96             },
97         }
99         match writing_mode {
100             SpecifiedWritingMode::HorizontalTb => {
101                 if direction == Direction::Rtl {
102                     flags.insert(WritingMode::INLINE_REVERSED);
103                 }
104             },
105             SpecifiedWritingMode::VerticalRl => {
106                 flags.insert(WritingMode::VERTICAL);
107                 if direction == Direction::Rtl {
108                     flags.insert(WritingMode::INLINE_REVERSED);
109                 }
110             },
111             SpecifiedWritingMode::VerticalLr => {
112                 flags.insert(WritingMode::VERTICAL);
113                 flags.insert(WritingMode::VERTICAL_LR);
114                 flags.insert(WritingMode::LINE_INVERTED);
115                 if direction == Direction::Rtl {
116                     flags.insert(WritingMode::INLINE_REVERSED);
117                 }
118             },
119             #[cfg(feature = "gecko")]
120             SpecifiedWritingMode::SidewaysRl => {
121                 flags.insert(WritingMode::VERTICAL);
122                 flags.insert(WritingMode::VERTICAL_SIDEWAYS);
123                 if direction == Direction::Rtl {
124                     flags.insert(WritingMode::INLINE_REVERSED);
125                 }
126             },
127             #[cfg(feature = "gecko")]
128             SpecifiedWritingMode::SidewaysLr => {
129                 flags.insert(WritingMode::VERTICAL);
130                 flags.insert(WritingMode::VERTICAL_LR);
131                 flags.insert(WritingMode::VERTICAL_SIDEWAYS);
132                 if direction == Direction::Ltr {
133                     flags.insert(WritingMode::INLINE_REVERSED);
134                 }
135             },
136         }
138         #[cfg(feature = "gecko")]
139         {
140             use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation;
142             // text-orientation only has an effect for vertical-rl and
143             // vertical-lr values of writing-mode.
144             match writing_mode {
145                 SpecifiedWritingMode::VerticalRl | SpecifiedWritingMode::VerticalLr => {
146                     match inheritedbox_style.clone_text_orientation() {
147                         TextOrientation::Mixed => {},
148                         TextOrientation::Upright => {
149                             flags.insert(WritingMode::UPRIGHT);
151                             // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright:
152                             //
153                             // > This value causes the used value of direction
154                             // > to be ltr, and for the purposes of bidi
155                             // > reordering, causes all characters to be treated
156                             // > as strong LTR.
157                             flags.remove(WritingMode::RTL);
158                             flags.remove(WritingMode::INLINE_REVERSED);
159                         },
160                         TextOrientation::Sideways => {
161                             flags.insert(WritingMode::TEXT_SIDEWAYS);
162                         },
163                     }
164                 },
165                 _ => {},
166             }
167         }
169         flags
170     }
172     /// Returns the `horizontal-tb` value.
173     pub fn horizontal_tb() -> Self {
174         Self::empty()
175     }
177     #[inline]
178     pub fn is_vertical(&self) -> bool {
179         self.intersects(WritingMode::VERTICAL)
180     }
182     #[inline]
183     pub fn is_horizontal(&self) -> bool {
184         !self.is_vertical()
185     }
187     /// Assuming .is_vertical(), does the block direction go left to right?
188     #[inline]
189     pub fn is_vertical_lr(&self) -> bool {
190         self.intersects(WritingMode::VERTICAL_LR)
191     }
193     /// Assuming .is_vertical(), does the inline direction go top to bottom?
194     #[inline]
195     pub fn is_inline_tb(&self) -> bool {
196         // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
197         !self.intersects(WritingMode::INLINE_REVERSED)
198     }
200     #[inline]
201     pub fn is_bidi_ltr(&self) -> bool {
202         !self.intersects(WritingMode::RTL)
203     }
205     #[inline]
206     pub fn is_sideways(&self) -> bool {
207         self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS)
208     }
210     #[inline]
211     pub fn is_upright(&self) -> bool {
212         self.intersects(WritingMode::UPRIGHT)
213     }
215     /// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
216     ///
217     /// | Return  | line-left is… | line-right is… |
218     /// |---------|---------------|----------------|
219     /// | `true`  | inline-start  | inline-end     |
220     /// | `false` | inline-end    | inline-start   |
221     #[inline]
222     pub fn line_left_is_inline_start(&self) -> bool {
223         // https://drafts.csswg.org/css-writing-modes/#inline-start
224         // “For boxes with a used direction value of ltr, this means the line-left side.
225         //  For boxes with a used direction value of rtl, this means the line-right side.”
226         self.is_bidi_ltr()
227     }
229     #[inline]
230     pub fn inline_start_physical_side(&self) -> PhysicalSide {
231         match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
232             (false, _, true) => PhysicalSide::Left,
233             (false, _, false) => PhysicalSide::Right,
234             (true, true, _) => PhysicalSide::Top,
235             (true, false, _) => PhysicalSide::Bottom,
236         }
237     }
239     #[inline]
240     pub fn inline_end_physical_side(&self) -> PhysicalSide {
241         match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
242             (false, _, true) => PhysicalSide::Right,
243             (false, _, false) => PhysicalSide::Left,
244             (true, true, _) => PhysicalSide::Bottom,
245             (true, false, _) => PhysicalSide::Top,
246         }
247     }
249     #[inline]
250     pub fn block_start_physical_side(&self) -> PhysicalSide {
251         match (self.is_vertical(), self.is_vertical_lr()) {
252             (false, _) => PhysicalSide::Top,
253             (true, true) => PhysicalSide::Left,
254             (true, false) => PhysicalSide::Right,
255         }
256     }
258     #[inline]
259     pub fn block_end_physical_side(&self) -> PhysicalSide {
260         match (self.is_vertical(), self.is_vertical_lr()) {
261             (false, _) => PhysicalSide::Bottom,
262             (true, true) => PhysicalSide::Right,
263             (true, false) => PhysicalSide::Left,
264         }
265     }
267     #[inline]
268     fn physical_sides_to_corner(
269         block_side: PhysicalSide,
270         inline_side: PhysicalSide,
271     ) -> PhysicalCorner {
272         match (block_side, inline_side) {
273             (PhysicalSide::Top, PhysicalSide::Left) | (PhysicalSide::Left, PhysicalSide::Top) => {
274                 PhysicalCorner::TopLeft
275             },
276             (PhysicalSide::Top, PhysicalSide::Right) | (PhysicalSide::Right, PhysicalSide::Top) => {
277                 PhysicalCorner::TopRight
278             },
279             (PhysicalSide::Bottom, PhysicalSide::Right) |
280             (PhysicalSide::Right, PhysicalSide::Bottom) => PhysicalCorner::BottomRight,
281             (PhysicalSide::Bottom, PhysicalSide::Left) |
282             (PhysicalSide::Left, PhysicalSide::Bottom) => PhysicalCorner::BottomLeft,
283             _ => unreachable!("block and inline sides must be orthogonal"),
284         }
285     }
287     #[inline]
288     pub fn start_start_physical_corner(&self) -> PhysicalCorner {
289         WritingMode::physical_sides_to_corner(
290             self.block_start_physical_side(),
291             self.inline_start_physical_side(),
292         )
293     }
295     #[inline]
296     pub fn start_end_physical_corner(&self) -> PhysicalCorner {
297         WritingMode::physical_sides_to_corner(
298             self.block_start_physical_side(),
299             self.inline_end_physical_side(),
300         )
301     }
303     #[inline]
304     pub fn end_start_physical_corner(&self) -> PhysicalCorner {
305         WritingMode::physical_sides_to_corner(
306             self.block_end_physical_side(),
307             self.inline_start_physical_side(),
308         )
309     }
311     #[inline]
312     pub fn end_end_physical_corner(&self) -> PhysicalCorner {
313         WritingMode::physical_sides_to_corner(
314             self.block_end_physical_side(),
315             self.inline_end_physical_side(),
316         )
317     }
319     #[inline]
320     pub fn block_flow_direction(&self) -> BlockFlowDirection {
321         match (self.is_vertical(), self.is_vertical_lr()) {
322             (false, _) => BlockFlowDirection::TopToBottom,
323             (true, true) => BlockFlowDirection::LeftToRight,
324             (true, false) => BlockFlowDirection::RightToLeft,
325         }
326     }
328     #[inline]
329     pub fn inline_base_direction(&self) -> InlineBaseDirection {
330         if self.intersects(WritingMode::RTL) {
331             InlineBaseDirection::RightToLeft
332         } else {
333             InlineBaseDirection::LeftToRight
334         }
335     }
337     #[inline]
338     /// The default bidirectional embedding level for this writing mode.
339     ///
340     /// Returns bidi level 0 if the mode is LTR, or 1 otherwise.
341     pub fn to_bidi_level(&self) -> bidi::Level {
342         if self.is_bidi_ltr() {
343             bidi::Level::ltr()
344         } else {
345             bidi::Level::rtl()
346         }
347     }
350 impl fmt::Display for WritingMode {
351     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
352         if self.is_vertical() {
353             write!(formatter, "V")?;
354             if self.is_vertical_lr() {
355                 write!(formatter, " LR")?;
356             } else {
357                 write!(formatter, " RL")?;
358             }
359             if self.is_sideways() {
360                 write!(formatter, " Sideways")?;
361             }
362             if self.intersects(WritingMode::LINE_INVERTED) {
363                 write!(formatter, " Inverted")?;
364             }
365         } else {
366             write!(formatter, "H")?;
367         }
368         if self.is_bidi_ltr() {
369             write!(formatter, " LTR")
370         } else {
371             write!(formatter, " RTL")
372         }
373     }
376 /// Wherever logical geometry is used, the writing mode is known based on context:
377 /// every method takes a `mode` parameter.
378 /// However, this context is easy to get wrong.
379 /// In debug builds only, logical geometry objects store their writing mode
380 /// (in addition to taking it as a parameter to methods) and check it.
381 /// In non-debug builds, make this storage zero-size and the checks no-ops.
382 #[cfg(not(debug_assertions))]
383 #[derive(Clone, Copy, Eq, PartialEq)]
384 #[cfg_attr(feature = "servo", derive(Serialize))]
385 struct DebugWritingMode;
387 #[cfg(debug_assertions)]
388 #[derive(Clone, Copy, Eq, PartialEq)]
389 #[cfg_attr(feature = "servo", derive(Serialize))]
390 struct DebugWritingMode {
391     mode: WritingMode,
394 #[cfg(not(debug_assertions))]
395 impl DebugWritingMode {
396     #[inline]
397     fn check(&self, _other: WritingMode) {}
399     #[inline]
400     fn check_debug(&self, _other: DebugWritingMode) {}
402     #[inline]
403     fn new(_mode: WritingMode) -> DebugWritingMode {
404         DebugWritingMode
405     }
408 #[cfg(debug_assertions)]
409 impl DebugWritingMode {
410     #[inline]
411     fn check(&self, other: WritingMode) {
412         assert_eq!(self.mode, other)
413     }
415     #[inline]
416     fn check_debug(&self, other: DebugWritingMode) {
417         assert_eq!(self.mode, other.mode)
418     }
420     #[inline]
421     fn new(mode: WritingMode) -> DebugWritingMode {
422         DebugWritingMode { mode }
423     }
426 impl Debug for DebugWritingMode {
427     #[cfg(not(debug_assertions))]
428     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
429         write!(formatter, "?")
430     }
432     #[cfg(debug_assertions)]
433     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
434         write!(formatter, "{}", self.mode)
435     }
438 // Used to specify the logical direction.
439 #[derive(Clone, Copy, Debug, PartialEq)]
440 #[cfg_attr(feature = "servo", derive(Serialize))]
441 pub enum Direction {
442     Inline,
443     Block,
446 /// A 2D size in flow-relative dimensions
447 #[derive(Clone, Copy, Eq, PartialEq)]
448 #[cfg_attr(feature = "servo", derive(Serialize))]
449 pub struct LogicalSize<T> {
450     pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure
451     pub block: T,  // block-size, a.k.a. logical height, a.k.a. extent
452     debug_writing_mode: DebugWritingMode,
455 impl<T: Debug> Debug for LogicalSize<T> {
456     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
457         write!(
458             formatter,
459             "LogicalSize({:?}, i{:?}×b{:?})",
460             self.debug_writing_mode, self.inline, self.block
461         )
462     }
465 // Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
466 impl<T: Zero> LogicalSize<T> {
467     #[inline]
468     pub fn zero(mode: WritingMode) -> LogicalSize<T> {
469         LogicalSize {
470             inline: Zero::zero(),
471             block: Zero::zero(),
472             debug_writing_mode: DebugWritingMode::new(mode),
473         }
474     }
477 impl<T> LogicalSize<T> {
478     #[inline]
479     pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {
480         LogicalSize {
481             inline: inline,
482             block: block,
483             debug_writing_mode: DebugWritingMode::new(mode),
484         }
485     }
487     #[inline]
488     pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {
489         if mode.is_vertical() {
490             LogicalSize::new(mode, size.height, size.width)
491         } else {
492             LogicalSize::new(mode, size.width, size.height)
493         }
494     }
497 impl<T: Copy> LogicalSize<T> {
498     #[inline]
499     pub fn width(&self, mode: WritingMode) -> T {
500         self.debug_writing_mode.check(mode);
501         if mode.is_vertical() {
502             self.block
503         } else {
504             self.inline
505         }
506     }
508     #[inline]
509     pub fn set_width(&mut self, mode: WritingMode, width: T) {
510         self.debug_writing_mode.check(mode);
511         if mode.is_vertical() {
512             self.block = width
513         } else {
514             self.inline = width
515         }
516     }
518     #[inline]
519     pub fn height(&self, mode: WritingMode) -> T {
520         self.debug_writing_mode.check(mode);
521         if mode.is_vertical() {
522             self.inline
523         } else {
524             self.block
525         }
526     }
528     #[inline]
529     pub fn set_height(&mut self, mode: WritingMode, height: T) {
530         self.debug_writing_mode.check(mode);
531         if mode.is_vertical() {
532             self.inline = height
533         } else {
534             self.block = height
535         }
536     }
538     #[inline]
539     pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
540         self.debug_writing_mode.check(mode);
541         if mode.is_vertical() {
542             Size2D::new(self.block, self.inline)
543         } else {
544             Size2D::new(self.inline, self.block)
545         }
546     }
548     #[inline]
549     pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {
550         if mode_from == mode_to {
551             self.debug_writing_mode.check(mode_from);
552             *self
553         } else {
554             LogicalSize::from_physical(mode_to, self.to_physical(mode_from))
555         }
556     }
559 impl<T: Add<T, Output = T>> Add for LogicalSize<T> {
560     type Output = LogicalSize<T>;
562     #[inline]
563     fn add(self, other: LogicalSize<T>) -> LogicalSize<T> {
564         self.debug_writing_mode
565             .check_debug(other.debug_writing_mode);
566         LogicalSize {
567             debug_writing_mode: self.debug_writing_mode,
568             inline: self.inline + other.inline,
569             block: self.block + other.block,
570         }
571     }
574 impl<T: Sub<T, Output = T>> Sub for LogicalSize<T> {
575     type Output = LogicalSize<T>;
577     #[inline]
578     fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> {
579         self.debug_writing_mode
580             .check_debug(other.debug_writing_mode);
581         LogicalSize {
582             debug_writing_mode: self.debug_writing_mode,
583             inline: self.inline - other.inline,
584             block: self.block - other.block,
585         }
586     }
589 /// A 2D point in flow-relative dimensions
590 #[derive(Clone, Copy, Eq, PartialEq)]
591 #[cfg_attr(feature = "servo", derive(Serialize))]
592 pub struct LogicalPoint<T> {
593     /// inline-axis coordinate
594     pub i: T,
595     /// block-axis coordinate
596     pub b: T,
597     debug_writing_mode: DebugWritingMode,
600 impl<T: Debug> Debug for LogicalPoint<T> {
601     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
602         write!(
603             formatter,
604             "LogicalPoint({:?} (i{:?}, b{:?}))",
605             self.debug_writing_mode, self.i, self.b
606         )
607     }
610 // Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
611 impl<T: Zero> LogicalPoint<T> {
612     #[inline]
613     pub fn zero(mode: WritingMode) -> LogicalPoint<T> {
614         LogicalPoint {
615             i: Zero::zero(),
616             b: Zero::zero(),
617             debug_writing_mode: DebugWritingMode::new(mode),
618         }
619     }
622 impl<T: Copy> LogicalPoint<T> {
623     #[inline]
624     pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {
625         LogicalPoint {
626             i: i,
627             b: b,
628             debug_writing_mode: DebugWritingMode::new(mode),
629         }
630     }
633 impl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> {
634     #[inline]
635     pub fn from_physical(
636         mode: WritingMode,
637         point: Point2D<T>,
638         container_size: Size2D<T>,
639     ) -> LogicalPoint<T> {
640         if mode.is_vertical() {
641             LogicalPoint {
642                 i: if mode.is_inline_tb() {
643                     point.y
644                 } else {
645                     container_size.height - point.y
646                 },
647                 b: if mode.is_vertical_lr() {
648                     point.x
649                 } else {
650                     container_size.width - point.x
651                 },
652                 debug_writing_mode: DebugWritingMode::new(mode),
653             }
654         } else {
655             LogicalPoint {
656                 i: if mode.is_bidi_ltr() {
657                     point.x
658                 } else {
659                     container_size.width - point.x
660                 },
661                 b: point.y,
662                 debug_writing_mode: DebugWritingMode::new(mode),
663             }
664         }
665     }
667     #[inline]
668     pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
669         self.debug_writing_mode.check(mode);
670         if mode.is_vertical() {
671             if mode.is_vertical_lr() {
672                 self.b
673             } else {
674                 container_size.width - self.b
675             }
676         } else {
677             if mode.is_bidi_ltr() {
678                 self.i
679             } else {
680                 container_size.width - self.i
681             }
682         }
683     }
685     #[inline]
686     pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {
687         self.debug_writing_mode.check(mode);
688         if mode.is_vertical() {
689             self.b = if mode.is_vertical_lr() {
690                 x
691             } else {
692                 container_size.width - x
693             }
694         } else {
695             self.i = if mode.is_bidi_ltr() {
696                 x
697             } else {
698                 container_size.width - x
699             }
700         }
701     }
703     #[inline]
704     pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
705         self.debug_writing_mode.check(mode);
706         if mode.is_vertical() {
707             if mode.is_inline_tb() {
708                 self.i
709             } else {
710                 container_size.height - self.i
711             }
712         } else {
713             self.b
714         }
715     }
717     #[inline]
718     pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {
719         self.debug_writing_mode.check(mode);
720         if mode.is_vertical() {
721             self.i = if mode.is_inline_tb() {
722                 y
723             } else {
724                 container_size.height - y
725             }
726         } else {
727             self.b = y
728         }
729     }
731     #[inline]
732     pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
733         self.debug_writing_mode.check(mode);
734         if mode.is_vertical() {
735             Point2D::new(
736                 if mode.is_vertical_lr() {
737                     self.b
738                 } else {
739                     container_size.width - self.b
740                 },
741                 if mode.is_inline_tb() {
742                     self.i
743                 } else {
744                     container_size.height - self.i
745                 },
746             )
747         } else {
748             Point2D::new(
749                 if mode.is_bidi_ltr() {
750                     self.i
751                 } else {
752                     container_size.width - self.i
753                 },
754                 self.b,
755             )
756         }
757     }
759     #[inline]
760     pub fn convert(
761         &self,
762         mode_from: WritingMode,
763         mode_to: WritingMode,
764         container_size: Size2D<T>,
765     ) -> LogicalPoint<T> {
766         if mode_from == mode_to {
767             self.debug_writing_mode.check(mode_from);
768             *self
769         } else {
770             LogicalPoint::from_physical(
771                 mode_to,
772                 self.to_physical(mode_from, container_size),
773                 container_size,
774             )
775         }
776     }
779 impl<T: Copy + Add<T, Output = T>> LogicalPoint<T> {
780     /// This doesn’t really makes sense,
781     /// but happens when dealing with multiple origins.
782     #[inline]
783     pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {
784         self.debug_writing_mode
785             .check_debug(other.debug_writing_mode);
786         LogicalPoint {
787             debug_writing_mode: self.debug_writing_mode,
788             i: self.i + other.i,
789             b: self.b + other.b,
790         }
791     }
794 impl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> {
795     type Output = LogicalPoint<T>;
797     #[inline]
798     fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> {
799         self.debug_writing_mode
800             .check_debug(other.debug_writing_mode);
801         LogicalPoint {
802             debug_writing_mode: self.debug_writing_mode,
803             i: self.i + other.inline,
804             b: self.b + other.block,
805         }
806     }
809 impl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> {
810     type Output = LogicalPoint<T>;
812     #[inline]
813     fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> {
814         self.debug_writing_mode
815             .check_debug(other.debug_writing_mode);
816         LogicalPoint {
817             debug_writing_mode: self.debug_writing_mode,
818             i: self.i - other.inline,
819             b: self.b - other.block,
820         }
821     }
824 /// A "margin" in flow-relative dimensions
825 /// Represents the four sides of the margins, borders, or padding of a CSS box,
826 /// or a combination of those.
827 /// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
828 #[derive(Clone, Copy, Eq, PartialEq)]
829 #[cfg_attr(feature = "servo", derive(Serialize))]
830 pub struct LogicalMargin<T> {
831     pub block_start: T,
832     pub inline_end: T,
833     pub block_end: T,
834     pub inline_start: T,
835     debug_writing_mode: DebugWritingMode,
838 impl<T: Debug> Debug for LogicalMargin<T> {
839     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
840         let writing_mode_string = if cfg!(debug_assertions) {
841             format!("{:?}, ", self.debug_writing_mode)
842         } else {
843             "".to_owned()
844         };
846         write!(
847             formatter,
848             "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})",
849             writing_mode_string,
850             self.inline_start,
851             self.inline_end,
852             self.block_start,
853             self.block_end
854         )
855     }
858 impl<T: Zero> LogicalMargin<T> {
859     #[inline]
860     pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
861         LogicalMargin {
862             block_start: Zero::zero(),
863             inline_end: Zero::zero(),
864             block_end: Zero::zero(),
865             inline_start: Zero::zero(),
866             debug_writing_mode: DebugWritingMode::new(mode),
867         }
868     }
871 impl<T> LogicalMargin<T> {
872     #[inline]
873     pub fn new(
874         mode: WritingMode,
875         block_start: T,
876         inline_end: T,
877         block_end: T,
878         inline_start: T,
879     ) -> LogicalMargin<T> {
880         LogicalMargin {
881             block_start,
882             inline_end,
883             block_end,
884             inline_start,
885             debug_writing_mode: DebugWritingMode::new(mode),
886         }
887     }
889     #[inline]
890     pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
891         let block_start;
892         let inline_end;
893         let block_end;
894         let inline_start;
895         if mode.is_vertical() {
896             if mode.is_vertical_lr() {
897                 block_start = offsets.left;
898                 block_end = offsets.right;
899             } else {
900                 block_start = offsets.right;
901                 block_end = offsets.left;
902             }
903             if mode.is_inline_tb() {
904                 inline_start = offsets.top;
905                 inline_end = offsets.bottom;
906             } else {
907                 inline_start = offsets.bottom;
908                 inline_end = offsets.top;
909             }
910         } else {
911             block_start = offsets.top;
912             block_end = offsets.bottom;
913             if mode.is_bidi_ltr() {
914                 inline_start = offsets.left;
915                 inline_end = offsets.right;
916             } else {
917                 inline_start = offsets.right;
918                 inline_end = offsets.left;
919             }
920         }
921         LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)
922     }
925 impl<T: Copy> LogicalMargin<T> {
926     #[inline]
927     pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {
928         LogicalMargin::new(mode, value, value, value, value)
929     }
931     #[inline]
932     pub fn top(&self, mode: WritingMode) -> T {
933         self.debug_writing_mode.check(mode);
934         if mode.is_vertical() {
935             if mode.is_inline_tb() {
936                 self.inline_start
937             } else {
938                 self.inline_end
939             }
940         } else {
941             self.block_start
942         }
943     }
945     #[inline]
946     pub fn set_top(&mut self, mode: WritingMode, top: T) {
947         self.debug_writing_mode.check(mode);
948         if mode.is_vertical() {
949             if mode.is_inline_tb() {
950                 self.inline_start = top
951             } else {
952                 self.inline_end = top
953             }
954         } else {
955             self.block_start = top
956         }
957     }
959     #[inline]
960     pub fn right(&self, mode: WritingMode) -> T {
961         self.debug_writing_mode.check(mode);
962         if mode.is_vertical() {
963             if mode.is_vertical_lr() {
964                 self.block_end
965             } else {
966                 self.block_start
967             }
968         } else {
969             if mode.is_bidi_ltr() {
970                 self.inline_end
971             } else {
972                 self.inline_start
973             }
974         }
975     }
977     #[inline]
978     pub fn set_right(&mut self, mode: WritingMode, right: T) {
979         self.debug_writing_mode.check(mode);
980         if mode.is_vertical() {
981             if mode.is_vertical_lr() {
982                 self.block_end = right
983             } else {
984                 self.block_start = right
985             }
986         } else {
987             if mode.is_bidi_ltr() {
988                 self.inline_end = right
989             } else {
990                 self.inline_start = right
991             }
992         }
993     }
995     #[inline]
996     pub fn bottom(&self, mode: WritingMode) -> T {
997         self.debug_writing_mode.check(mode);
998         if mode.is_vertical() {
999             if mode.is_inline_tb() {
1000                 self.inline_end
1001             } else {
1002                 self.inline_start
1003             }
1004         } else {
1005             self.block_end
1006         }
1007     }
1009     #[inline]
1010     pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
1011         self.debug_writing_mode.check(mode);
1012         if mode.is_vertical() {
1013             if mode.is_inline_tb() {
1014                 self.inline_end = bottom
1015             } else {
1016                 self.inline_start = bottom
1017             }
1018         } else {
1019             self.block_end = bottom
1020         }
1021     }
1023     #[inline]
1024     pub fn left(&self, mode: WritingMode) -> T {
1025         self.debug_writing_mode.check(mode);
1026         if mode.is_vertical() {
1027             if mode.is_vertical_lr() {
1028                 self.block_start
1029             } else {
1030                 self.block_end
1031             }
1032         } else {
1033             if mode.is_bidi_ltr() {
1034                 self.inline_start
1035             } else {
1036                 self.inline_end
1037             }
1038         }
1039     }
1041     #[inline]
1042     pub fn set_left(&mut self, mode: WritingMode, left: T) {
1043         self.debug_writing_mode.check(mode);
1044         if mode.is_vertical() {
1045             if mode.is_vertical_lr() {
1046                 self.block_start = left
1047             } else {
1048                 self.block_end = left
1049             }
1050         } else {
1051             if mode.is_bidi_ltr() {
1052                 self.inline_start = left
1053             } else {
1054                 self.inline_end = left
1055             }
1056         }
1057     }
1059     #[inline]
1060     pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
1061         self.debug_writing_mode.check(mode);
1062         let top;
1063         let right;
1064         let bottom;
1065         let left;
1066         if mode.is_vertical() {
1067             if mode.is_vertical_lr() {
1068                 left = self.block_start;
1069                 right = self.block_end;
1070             } else {
1071                 right = self.block_start;
1072                 left = self.block_end;
1073             }
1074             if mode.is_inline_tb() {
1075                 top = self.inline_start;
1076                 bottom = self.inline_end;
1077             } else {
1078                 bottom = self.inline_start;
1079                 top = self.inline_end;
1080             }
1081         } else {
1082             top = self.block_start;
1083             bottom = self.block_end;
1084             if mode.is_bidi_ltr() {
1085                 left = self.inline_start;
1086                 right = self.inline_end;
1087             } else {
1088                 right = self.inline_start;
1089                 left = self.inline_end;
1090             }
1091         }
1092         SideOffsets2D::new(top, right, bottom, left)
1093     }
1095     #[inline]
1096     pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
1097         if mode_from == mode_to {
1098             self.debug_writing_mode.check(mode_from);
1099             *self
1100         } else {
1101             LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
1102         }
1103     }
1106 impl<T: PartialEq + Zero> LogicalMargin<T> {
1107     #[inline]
1108     pub fn is_zero(&self) -> bool {
1109         self.block_start == Zero::zero() &&
1110             self.inline_end == Zero::zero() &&
1111             self.block_end == Zero::zero() &&
1112             self.inline_start == Zero::zero()
1113     }
1116 impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {
1117     #[inline]
1118     pub fn inline_start_end(&self) -> T {
1119         self.inline_start + self.inline_end
1120     }
1122     #[inline]
1123     pub fn block_start_end(&self) -> T {
1124         self.block_start + self.block_end
1125     }
1127     #[inline]
1128     pub fn start_end(&self, direction: Direction) -> T {
1129         match direction {
1130             Direction::Inline => self.inline_start + self.inline_end,
1131             Direction::Block => self.block_start + self.block_end,
1132         }
1133     }
1135     #[inline]
1136     pub fn top_bottom(&self, mode: WritingMode) -> T {
1137         self.debug_writing_mode.check(mode);
1138         if mode.is_vertical() {
1139             self.inline_start_end()
1140         } else {
1141             self.block_start_end()
1142         }
1143     }
1145     #[inline]
1146     pub fn left_right(&self, mode: WritingMode) -> T {
1147         self.debug_writing_mode.check(mode);
1148         if mode.is_vertical() {
1149             self.block_start_end()
1150         } else {
1151             self.inline_start_end()
1152         }
1153     }
1156 impl<T: Add<T, Output = T>> Add for LogicalMargin<T> {
1157     type Output = LogicalMargin<T>;
1159     #[inline]
1160     fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1161         self.debug_writing_mode
1162             .check_debug(other.debug_writing_mode);
1163         LogicalMargin {
1164             debug_writing_mode: self.debug_writing_mode,
1165             block_start: self.block_start + other.block_start,
1166             inline_end: self.inline_end + other.inline_end,
1167             block_end: self.block_end + other.block_end,
1168             inline_start: self.inline_start + other.inline_start,
1169         }
1170     }
1173 impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {
1174     type Output = LogicalMargin<T>;
1176     #[inline]
1177     fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
1178         self.debug_writing_mode
1179             .check_debug(other.debug_writing_mode);
1180         LogicalMargin {
1181             debug_writing_mode: self.debug_writing_mode,
1182             block_start: self.block_start - other.block_start,
1183             inline_end: self.inline_end - other.inline_end,
1184             block_end: self.block_end - other.block_end,
1185             inline_start: self.inline_start - other.inline_start,
1186         }
1187     }
1190 /// A rectangle in flow-relative dimensions
1191 #[derive(Clone, Copy, Eq, PartialEq)]
1192 #[cfg_attr(feature = "servo", derive(Serialize))]
1193 pub struct LogicalRect<T> {
1194     pub start: LogicalPoint<T>,
1195     pub size: LogicalSize<T>,
1196     debug_writing_mode: DebugWritingMode,
1199 impl<T: Debug> Debug for LogicalRect<T> {
1200     fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
1201         let writing_mode_string = if cfg!(debug_assertions) {
1202             format!("{:?}, ", self.debug_writing_mode)
1203         } else {
1204             "".to_owned()
1205         };
1207         write!(
1208             formatter,
1209             "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))",
1210             writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b
1211         )
1212     }
1215 impl<T: Zero> LogicalRect<T> {
1216     #[inline]
1217     pub fn zero(mode: WritingMode) -> LogicalRect<T> {
1218         LogicalRect {
1219             start: LogicalPoint::zero(mode),
1220             size: LogicalSize::zero(mode),
1221             debug_writing_mode: DebugWritingMode::new(mode),
1222         }
1223     }
1226 impl<T: Copy> LogicalRect<T> {
1227     #[inline]
1228     pub fn new(
1229         mode: WritingMode,
1230         inline_start: T,
1231         block_start: T,
1232         inline: T,
1233         block: T,
1234     ) -> LogicalRect<T> {
1235         LogicalRect {
1236             start: LogicalPoint::new(mode, inline_start, block_start),
1237             size: LogicalSize::new(mode, inline, block),
1238             debug_writing_mode: DebugWritingMode::new(mode),
1239         }
1240     }
1242     #[inline]
1243     pub fn from_point_size(
1244         mode: WritingMode,
1245         start: LogicalPoint<T>,
1246         size: LogicalSize<T>,
1247     ) -> LogicalRect<T> {
1248         start.debug_writing_mode.check(mode);
1249         size.debug_writing_mode.check(mode);
1250         LogicalRect {
1251             start: start,
1252             size: size,
1253             debug_writing_mode: DebugWritingMode::new(mode),
1254         }
1255     }
1258 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1259     #[inline]
1260     pub fn from_physical(
1261         mode: WritingMode,
1262         rect: Rect<T>,
1263         container_size: Size2D<T>,
1264     ) -> LogicalRect<T> {
1265         let inline_start;
1266         let block_start;
1267         let inline;
1268         let block;
1269         if mode.is_vertical() {
1270             inline = rect.size.height;
1271             block = rect.size.width;
1272             if mode.is_vertical_lr() {
1273                 block_start = rect.origin.x;
1274             } else {
1275                 block_start = container_size.width - (rect.origin.x + rect.size.width);
1276             }
1277             if mode.is_inline_tb() {
1278                 inline_start = rect.origin.y;
1279             } else {
1280                 inline_start = container_size.height - (rect.origin.y + rect.size.height);
1281             }
1282         } else {
1283             inline = rect.size.width;
1284             block = rect.size.height;
1285             block_start = rect.origin.y;
1286             if mode.is_bidi_ltr() {
1287                 inline_start = rect.origin.x;
1288             } else {
1289                 inline_start = container_size.width - (rect.origin.x + rect.size.width);
1290             }
1291         }
1292         LogicalRect {
1293             start: LogicalPoint::new(mode, inline_start, block_start),
1294             size: LogicalSize::new(mode, inline, block),
1295             debug_writing_mode: DebugWritingMode::new(mode),
1296         }
1297     }
1299     #[inline]
1300     pub fn inline_end(&self) -> T {
1301         self.start.i + self.size.inline
1302     }
1304     #[inline]
1305     pub fn block_end(&self) -> T {
1306         self.start.b + self.size.block
1307     }
1309     #[inline]
1310     pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
1311         self.debug_writing_mode.check(mode);
1312         let x;
1313         let y;
1314         let width;
1315         let height;
1316         if mode.is_vertical() {
1317             width = self.size.block;
1318             height = self.size.inline;
1319             if mode.is_vertical_lr() {
1320                 x = self.start.b;
1321             } else {
1322                 x = container_size.width - self.block_end();
1323             }
1324             if mode.is_inline_tb() {
1325                 y = self.start.i;
1326             } else {
1327                 y = container_size.height - self.inline_end();
1328             }
1329         } else {
1330             width = self.size.inline;
1331             height = self.size.block;
1332             y = self.start.b;
1333             if mode.is_bidi_ltr() {
1334                 x = self.start.i;
1335             } else {
1336                 x = container_size.width - self.inline_end();
1337             }
1338         }
1339         Rect {
1340             origin: Point2D::new(x, y),
1341             size: Size2D::new(width, height),
1342         }
1343     }
1345     #[inline]
1346     pub fn convert(
1347         &self,
1348         mode_from: WritingMode,
1349         mode_to: WritingMode,
1350         container_size: Size2D<T>,
1351     ) -> LogicalRect<T> {
1352         if mode_from == mode_to {
1353             self.debug_writing_mode.check(mode_from);
1354             *self
1355         } else {
1356             LogicalRect::from_physical(
1357                 mode_to,
1358                 self.to_physical(mode_from, container_size),
1359                 container_size,
1360             )
1361         }
1362     }
1364     pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {
1365         LogicalRect {
1366             start: self.start + offset,
1367             ..*self
1368         }
1369     }
1371     pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
1372         LogicalRect {
1373             start: self.start +
1374                 LogicalSize {
1375                     inline: offset.i,
1376                     block: offset.b,
1377                     debug_writing_mode: offset.debug_writing_mode,
1378                 },
1379             size: self.size,
1380             debug_writing_mode: self.debug_writing_mode,
1381         }
1382     }
1385 impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
1386     #[inline]
1387     pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
1388         self.debug_writing_mode
1389             .check_debug(other.debug_writing_mode);
1391         let inline_start = min(self.start.i, other.start.i);
1392         let block_start = min(self.start.b, other.start.b);
1393         LogicalRect {
1394             start: LogicalPoint {
1395                 i: inline_start,
1396                 b: block_start,
1397                 debug_writing_mode: self.debug_writing_mode,
1398             },
1399             size: LogicalSize {
1400                 inline: max(self.inline_end(), other.inline_end()) - inline_start,
1401                 block: max(self.block_end(), other.block_end()) - block_start,
1402                 debug_writing_mode: self.debug_writing_mode,
1403             },
1404             debug_writing_mode: self.debug_writing_mode,
1405         }
1406     }
1409 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {
1410     type Output = LogicalRect<T>;
1412     #[inline]
1413     fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1414         self.debug_writing_mode
1415             .check_debug(other.debug_writing_mode);
1416         LogicalRect {
1417             start: LogicalPoint {
1418                 // Growing a rectangle on the start side means pushing its
1419                 // start point on the negative direction.
1420                 i: self.start.i - other.inline_start,
1421                 b: self.start.b - other.block_start,
1422                 debug_writing_mode: self.debug_writing_mode,
1423             },
1424             size: LogicalSize {
1425                 inline: self.size.inline + other.inline_start_end(),
1426                 block: self.size.block + other.block_start_end(),
1427                 debug_writing_mode: self.debug_writing_mode,
1428             },
1429             debug_writing_mode: self.debug_writing_mode,
1430         }
1431     }
1434 impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {
1435     type Output = LogicalRect<T>;
1437     #[inline]
1438     fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {
1439         self.debug_writing_mode
1440             .check_debug(other.debug_writing_mode);
1441         LogicalRect {
1442             start: LogicalPoint {
1443                 // Shrinking a rectangle on the start side means pushing its
1444                 // start point on the positive direction.
1445                 i: self.start.i + other.inline_start,
1446                 b: self.start.b + other.block_start,
1447                 debug_writing_mode: self.debug_writing_mode,
1448             },
1449             size: LogicalSize {
1450                 inline: self.size.inline - other.inline_start_end(),
1451                 block: self.size.block - other.block_start_end(),
1452                 debug_writing_mode: self.debug_writing_mode,
1453             },
1454             debug_writing_mode: self.debug_writing_mode,
1455         }
1456     }
1459 #[derive(Clone, Copy, Debug, PartialEq)]
1460 pub enum PhysicalSide {
1461     Top,
1462     Right,
1463     Bottom,
1464     Left,
1467 #[derive(Clone, Copy, Debug, PartialEq)]
1468 pub enum PhysicalCorner {
1469     TopLeft,
1470     TopRight,
1471     BottomRight,
1472     BottomLeft,