forked from mirrors/gecko-dev
		
	Depends on D57314 Differential Revision: https://phabricator.services.mozilla.com/D57315 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			1467 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			1467 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						||
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 | 
						||
 | 
						||
//! Geometry in flow-relative space.
 | 
						||
 | 
						||
use crate::properties::style_structs;
 | 
						||
use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D};
 | 
						||
use euclid::num::Zero;
 | 
						||
use std::cmp::{max, min};
 | 
						||
use std::fmt::{self, Debug, Error, Formatter};
 | 
						||
use std::ops::{Add, Sub};
 | 
						||
use unicode_bidi as bidi;
 | 
						||
 | 
						||
pub enum BlockFlowDirection {
 | 
						||
    TopToBottom,
 | 
						||
    RightToLeft,
 | 
						||
    LeftToRight,
 | 
						||
}
 | 
						||
 | 
						||
pub enum InlineBaseDirection {
 | 
						||
    LeftToRight,
 | 
						||
    RightToLeft,
 | 
						||
}
 | 
						||
 | 
						||
// TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt()
 | 
						||
bitflags!(
 | 
						||
    #[cfg_attr(feature = "servo", derive(MallocSizeOf, Serialize))]
 | 
						||
    #[repr(C)]
 | 
						||
    pub struct WritingMode: u8 {
 | 
						||
        /// A vertical writing mode; writing-mode is vertical-rl,
 | 
						||
        /// vertical-lr, sideways-lr, or sideways-rl.
 | 
						||
        const VERTICAL = 1 << 0;
 | 
						||
        /// The inline flow direction is reversed against the physical
 | 
						||
        /// direction (i.e. right-to-left or bottom-to-top); writing-mode is
 | 
						||
        /// sideways-lr or direction is rtl (but not both).
 | 
						||
        ///
 | 
						||
        /// (This bit can be derived from the others, but we store it for
 | 
						||
        /// convenience.)
 | 
						||
        const INLINE_REVERSED = 1 << 1;
 | 
						||
        /// A vertical writing mode whose block progression direction is left-
 | 
						||
        /// to-right; writing-mode is vertical-lr or sideways-lr.
 | 
						||
        ///
 | 
						||
        /// Never set without VERTICAL.
 | 
						||
        const VERTICAL_LR = 1 << 2;
 | 
						||
        /// The line-over/line-under sides are inverted with respect to the
 | 
						||
        /// block-start/block-end edge; writing-mode is vertical-lr.
 | 
						||
        ///
 | 
						||
        /// Never set without VERTICAL and VERTICAL_LR.
 | 
						||
        const LINE_INVERTED = 1 << 3;
 | 
						||
        /// direction is rtl.
 | 
						||
        const RTL = 1 << 4;
 | 
						||
        /// All text within a vertical writing mode is displayed sideways
 | 
						||
        /// and runs top-to-bottom or bottom-to-top; set in these cases:
 | 
						||
        ///
 | 
						||
        /// * writing-mode: sideways-rl;
 | 
						||
        /// * writing-mode: sideways-lr;
 | 
						||
        ///
 | 
						||
        /// Never set without VERTICAL.
 | 
						||
        const VERTICAL_SIDEWAYS = 1 << 5;
 | 
						||
        /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation;
 | 
						||
        /// set in these cases:
 | 
						||
        ///
 | 
						||
        /// * writing-mode: vertical-rl; text-orientation: sideways;
 | 
						||
        /// * writing-mode: vertical-lr; text-orientation: sideways;
 | 
						||
        ///
 | 
						||
        /// Never set without VERTICAL.
 | 
						||
        const TEXT_SIDEWAYS = 1 << 6;
 | 
						||
        /// Horizontal text within a vertical writing mode is displayed with each
 | 
						||
        /// glyph upright; set in these cases:
 | 
						||
        ///
 | 
						||
        /// * writing-mode: vertical-rl; text-orientation: upright;
 | 
						||
        /// * writing-mode: vertical-lr: text-orientation: upright;
 | 
						||
        ///
 | 
						||
        /// Never set without VERTICAL.
 | 
						||
        const UPRIGHT = 1 << 7;
 | 
						||
    }
 | 
						||
);
 | 
						||
 | 
						||
impl WritingMode {
 | 
						||
    /// Return a WritingMode bitflags from the relevant CSS properties.
 | 
						||
    pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self {
 | 
						||
        use crate::properties::longhands::direction::computed_value::T as Direction;
 | 
						||
        use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;
 | 
						||
 | 
						||
        let mut flags = WritingMode::empty();
 | 
						||
 | 
						||
        let direction = inheritedbox_style.clone_direction();
 | 
						||
        let writing_mode = inheritedbox_style.clone_writing_mode();
 | 
						||
 | 
						||
        match direction {
 | 
						||
            Direction::Ltr => {},
 | 
						||
            Direction::Rtl => {
 | 
						||
                flags.insert(WritingMode::RTL);
 | 
						||
            },
 | 
						||
        }
 | 
						||
 | 
						||
        match writing_mode {
 | 
						||
            SpecifiedWritingMode::HorizontalTb => {
 | 
						||
                if direction == Direction::Rtl {
 | 
						||
                    flags.insert(WritingMode::INLINE_REVERSED);
 | 
						||
                }
 | 
						||
            },
 | 
						||
            SpecifiedWritingMode::VerticalRl => {
 | 
						||
                flags.insert(WritingMode::VERTICAL);
 | 
						||
                if direction == Direction::Rtl {
 | 
						||
                    flags.insert(WritingMode::INLINE_REVERSED);
 | 
						||
                }
 | 
						||
            },
 | 
						||
            SpecifiedWritingMode::VerticalLr => {
 | 
						||
                flags.insert(WritingMode::VERTICAL);
 | 
						||
                flags.insert(WritingMode::VERTICAL_LR);
 | 
						||
                flags.insert(WritingMode::LINE_INVERTED);
 | 
						||
                if direction == Direction::Rtl {
 | 
						||
                    flags.insert(WritingMode::INLINE_REVERSED);
 | 
						||
                }
 | 
						||
            },
 | 
						||
            #[cfg(feature = "gecko")]
 | 
						||
            SpecifiedWritingMode::SidewaysRl => {
 | 
						||
                flags.insert(WritingMode::VERTICAL);
 | 
						||
                flags.insert(WritingMode::VERTICAL_SIDEWAYS);
 | 
						||
                if direction == Direction::Rtl {
 | 
						||
                    flags.insert(WritingMode::INLINE_REVERSED);
 | 
						||
                }
 | 
						||
            },
 | 
						||
            #[cfg(feature = "gecko")]
 | 
						||
            SpecifiedWritingMode::SidewaysLr => {
 | 
						||
                flags.insert(WritingMode::VERTICAL);
 | 
						||
                flags.insert(WritingMode::VERTICAL_LR);
 | 
						||
                flags.insert(WritingMode::VERTICAL_SIDEWAYS);
 | 
						||
                if direction == Direction::Ltr {
 | 
						||
                    flags.insert(WritingMode::INLINE_REVERSED);
 | 
						||
                }
 | 
						||
            },
 | 
						||
        }
 | 
						||
 | 
						||
        #[cfg(feature = "gecko")]
 | 
						||
        {
 | 
						||
            use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation;
 | 
						||
 | 
						||
            // text-orientation only has an effect for vertical-rl and
 | 
						||
            // vertical-lr values of writing-mode.
 | 
						||
            match writing_mode {
 | 
						||
                SpecifiedWritingMode::VerticalRl | SpecifiedWritingMode::VerticalLr => {
 | 
						||
                    match inheritedbox_style.clone_text_orientation() {
 | 
						||
                        TextOrientation::Mixed => {},
 | 
						||
                        TextOrientation::Upright => {
 | 
						||
                            flags.insert(WritingMode::UPRIGHT);
 | 
						||
 | 
						||
                            // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright:
 | 
						||
                            //
 | 
						||
                            // > This value causes the used value of direction
 | 
						||
                            // > to be ltr, and for the purposes of bidi
 | 
						||
                            // > reordering, causes all characters to be treated
 | 
						||
                            // > as strong LTR.
 | 
						||
                            flags.remove(WritingMode::RTL);
 | 
						||
                            flags.remove(WritingMode::INLINE_REVERSED);
 | 
						||
                        },
 | 
						||
                        TextOrientation::Sideways => {
 | 
						||
                            flags.insert(WritingMode::TEXT_SIDEWAYS);
 | 
						||
                        },
 | 
						||
                    }
 | 
						||
                },
 | 
						||
                _ => {},
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        flags
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn is_vertical(&self) -> bool {
 | 
						||
        self.intersects(WritingMode::VERTICAL)
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn is_horizontal(&self) -> bool {
 | 
						||
        !self.is_vertical()
 | 
						||
    }
 | 
						||
 | 
						||
    /// Assuming .is_vertical(), does the block direction go left to right?
 | 
						||
    #[inline]
 | 
						||
    pub fn is_vertical_lr(&self) -> bool {
 | 
						||
        self.intersects(WritingMode::VERTICAL_LR)
 | 
						||
    }
 | 
						||
 | 
						||
    /// Assuming .is_vertical(), does the inline direction go top to bottom?
 | 
						||
    #[inline]
 | 
						||
    pub fn is_inline_tb(&self) -> bool {
 | 
						||
        // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical
 | 
						||
        !self.intersects(WritingMode::INLINE_REVERSED)
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn is_bidi_ltr(&self) -> bool {
 | 
						||
        !self.intersects(WritingMode::RTL)
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn is_sideways(&self) -> bool {
 | 
						||
        self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS)
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn is_upright(&self) -> bool {
 | 
						||
        self.intersects(WritingMode::UPRIGHT)
 | 
						||
    }
 | 
						||
 | 
						||
    /// https://drafts.csswg.org/css-writing-modes/#logical-to-physical
 | 
						||
    ///
 | 
						||
    /// | Return  | line-left is… | line-right is… |
 | 
						||
    /// |---------|---------------|----------------|
 | 
						||
    /// | `true`  | inline-start  | inline-end     |
 | 
						||
    /// | `false` | inline-end    | inline-start   |
 | 
						||
    #[inline]
 | 
						||
    pub fn line_left_is_inline_start(&self) -> bool {
 | 
						||
        // https://drafts.csswg.org/css-writing-modes/#inline-start
 | 
						||
        // “For boxes with a used direction value of ltr, this means the line-left side.
 | 
						||
        //  For boxes with a used direction value of rtl, this means the line-right side.”
 | 
						||
        self.is_bidi_ltr()
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn inline_start_physical_side(&self) -> PhysicalSide {
 | 
						||
        match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
 | 
						||
            (false, _, true) => PhysicalSide::Left,
 | 
						||
            (false, _, false) => PhysicalSide::Right,
 | 
						||
            (true, true, _) => PhysicalSide::Top,
 | 
						||
            (true, false, _) => PhysicalSide::Bottom,
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn inline_end_physical_side(&self) -> PhysicalSide {
 | 
						||
        match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {
 | 
						||
            (false, _, true) => PhysicalSide::Right,
 | 
						||
            (false, _, false) => PhysicalSide::Left,
 | 
						||
            (true, true, _) => PhysicalSide::Bottom,
 | 
						||
            (true, false, _) => PhysicalSide::Top,
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn block_start_physical_side(&self) -> PhysicalSide {
 | 
						||
        match (self.is_vertical(), self.is_vertical_lr()) {
 | 
						||
            (false, _) => PhysicalSide::Top,
 | 
						||
            (true, true) => PhysicalSide::Left,
 | 
						||
            (true, false) => PhysicalSide::Right,
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn block_end_physical_side(&self) -> PhysicalSide {
 | 
						||
        match (self.is_vertical(), self.is_vertical_lr()) {
 | 
						||
            (false, _) => PhysicalSide::Bottom,
 | 
						||
            (true, true) => PhysicalSide::Right,
 | 
						||
            (true, false) => PhysicalSide::Left,
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn physical_sides_to_corner(
 | 
						||
        block_side: PhysicalSide,
 | 
						||
        inline_side: PhysicalSide,
 | 
						||
    ) -> PhysicalCorner {
 | 
						||
        match (block_side, inline_side) {
 | 
						||
            (PhysicalSide::Top, PhysicalSide::Left) | (PhysicalSide::Left, PhysicalSide::Top) => {
 | 
						||
                PhysicalCorner::TopLeft
 | 
						||
            },
 | 
						||
            (PhysicalSide::Top, PhysicalSide::Right) | (PhysicalSide::Right, PhysicalSide::Top) => {
 | 
						||
                PhysicalCorner::TopRight
 | 
						||
            },
 | 
						||
            (PhysicalSide::Bottom, PhysicalSide::Right) |
 | 
						||
            (PhysicalSide::Right, PhysicalSide::Bottom) => PhysicalCorner::BottomRight,
 | 
						||
            (PhysicalSide::Bottom, PhysicalSide::Left) |
 | 
						||
            (PhysicalSide::Left, PhysicalSide::Bottom) => PhysicalCorner::BottomLeft,
 | 
						||
            _ => unreachable!("block and inline sides must be orthogonal"),
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn start_start_physical_corner(&self) -> PhysicalCorner {
 | 
						||
        WritingMode::physical_sides_to_corner(
 | 
						||
            self.block_start_physical_side(),
 | 
						||
            self.inline_start_physical_side(),
 | 
						||
        )
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn start_end_physical_corner(&self) -> PhysicalCorner {
 | 
						||
        WritingMode::physical_sides_to_corner(
 | 
						||
            self.block_start_physical_side(),
 | 
						||
            self.inline_end_physical_side(),
 | 
						||
        )
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn end_start_physical_corner(&self) -> PhysicalCorner {
 | 
						||
        WritingMode::physical_sides_to_corner(
 | 
						||
            self.block_end_physical_side(),
 | 
						||
            self.inline_start_physical_side(),
 | 
						||
        )
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn end_end_physical_corner(&self) -> PhysicalCorner {
 | 
						||
        WritingMode::physical_sides_to_corner(
 | 
						||
            self.block_end_physical_side(),
 | 
						||
            self.inline_end_physical_side(),
 | 
						||
        )
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn block_flow_direction(&self) -> BlockFlowDirection {
 | 
						||
        match (self.is_vertical(), self.is_vertical_lr()) {
 | 
						||
            (false, _) => BlockFlowDirection::TopToBottom,
 | 
						||
            (true, true) => BlockFlowDirection::LeftToRight,
 | 
						||
            (true, false) => BlockFlowDirection::RightToLeft,
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn inline_base_direction(&self) -> InlineBaseDirection {
 | 
						||
        if self.intersects(WritingMode::RTL) {
 | 
						||
            InlineBaseDirection::RightToLeft
 | 
						||
        } else {
 | 
						||
            InlineBaseDirection::LeftToRight
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    /// The default bidirectional embedding level for this writing mode.
 | 
						||
    ///
 | 
						||
    /// Returns bidi level 0 if the mode is LTR, or 1 otherwise.
 | 
						||
    pub fn to_bidi_level(&self) -> bidi::Level {
 | 
						||
        if self.is_bidi_ltr() {
 | 
						||
            bidi::Level::ltr()
 | 
						||
        } else {
 | 
						||
            bidi::Level::rtl()
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl fmt::Display for WritingMode {
 | 
						||
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
 | 
						||
        if self.is_vertical() {
 | 
						||
            write!(formatter, "V")?;
 | 
						||
            if self.is_vertical_lr() {
 | 
						||
                write!(formatter, " LR")?;
 | 
						||
            } else {
 | 
						||
                write!(formatter, " RL")?;
 | 
						||
            }
 | 
						||
            if self.is_sideways() {
 | 
						||
                write!(formatter, " Sideways")?;
 | 
						||
            }
 | 
						||
            if self.intersects(WritingMode::LINE_INVERTED) {
 | 
						||
                write!(formatter, " Inverted")?;
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            write!(formatter, "H")?;
 | 
						||
        }
 | 
						||
        if self.is_bidi_ltr() {
 | 
						||
            write!(formatter, " LTR")
 | 
						||
        } else {
 | 
						||
            write!(formatter, " RTL")
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/// Wherever logical geometry is used, the writing mode is known based on context:
 | 
						||
/// every method takes a `mode` parameter.
 | 
						||
/// However, this context is easy to get wrong.
 | 
						||
/// In debug builds only, logical geometry objects store their writing mode
 | 
						||
/// (in addition to taking it as a parameter to methods) and check it.
 | 
						||
/// In non-debug builds, make this storage zero-size and the checks no-ops.
 | 
						||
#[cfg(not(debug_assertions))]
 | 
						||
#[derive(Clone, Copy, Eq, PartialEq)]
 | 
						||
#[cfg_attr(feature = "servo", derive(Serialize))]
 | 
						||
struct DebugWritingMode;
 | 
						||
 | 
						||
#[cfg(debug_assertions)]
 | 
						||
#[derive(Clone, Copy, Eq, PartialEq)]
 | 
						||
#[cfg_attr(feature = "servo", derive(Serialize))]
 | 
						||
struct DebugWritingMode {
 | 
						||
    mode: WritingMode,
 | 
						||
}
 | 
						||
 | 
						||
#[cfg(not(debug_assertions))]
 | 
						||
impl DebugWritingMode {
 | 
						||
    #[inline]
 | 
						||
    fn check(&self, _other: WritingMode) {}
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn check_debug(&self, _other: DebugWritingMode) {}
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn new(_mode: WritingMode) -> DebugWritingMode {
 | 
						||
        DebugWritingMode
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
#[cfg(debug_assertions)]
 | 
						||
impl DebugWritingMode {
 | 
						||
    #[inline]
 | 
						||
    fn check(&self, other: WritingMode) {
 | 
						||
        assert_eq!(self.mode, other)
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn check_debug(&self, other: DebugWritingMode) {
 | 
						||
        assert_eq!(self.mode, other.mode)
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn new(mode: WritingMode) -> DebugWritingMode {
 | 
						||
        DebugWritingMode { mode: mode }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl Debug for DebugWritingMode {
 | 
						||
    #[cfg(not(debug_assertions))]
 | 
						||
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
 | 
						||
        write!(formatter, "?")
 | 
						||
    }
 | 
						||
 | 
						||
    #[cfg(debug_assertions)]
 | 
						||
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
 | 
						||
        write!(formatter, "{}", self.mode)
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
// Used to specify the logical direction.
 | 
						||
#[derive(Clone, Copy, Debug, PartialEq)]
 | 
						||
#[cfg_attr(feature = "servo", derive(Serialize))]
 | 
						||
pub enum Direction {
 | 
						||
    Inline,
 | 
						||
    Block,
 | 
						||
}
 | 
						||
 | 
						||
/// A 2D size in flow-relative dimensions
 | 
						||
#[derive(Clone, Copy, Eq, PartialEq)]
 | 
						||
#[cfg_attr(feature = "servo", derive(Serialize))]
 | 
						||
pub struct LogicalSize<T> {
 | 
						||
    pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure
 | 
						||
    pub block: T,  // block-size, a.k.a. logical height, a.k.a. extent
 | 
						||
    debug_writing_mode: DebugWritingMode,
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Debug> Debug for LogicalSize<T> {
 | 
						||
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
 | 
						||
        write!(
 | 
						||
            formatter,
 | 
						||
            "LogicalSize({:?}, i{:?}×b{:?})",
 | 
						||
            self.debug_writing_mode, self.inline, self.block
 | 
						||
        )
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
// Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
 | 
						||
impl<T: Zero> LogicalSize<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn zero(mode: WritingMode) -> LogicalSize<T> {
 | 
						||
        LogicalSize {
 | 
						||
            inline: Zero::zero(),
 | 
						||
            block: Zero::zero(),
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T> LogicalSize<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {
 | 
						||
        LogicalSize {
 | 
						||
            inline: inline,
 | 
						||
            block: block,
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {
 | 
						||
        if mode.is_vertical() {
 | 
						||
            LogicalSize::new(mode, size.height, size.width)
 | 
						||
        } else {
 | 
						||
            LogicalSize::new(mode, size.width, size.height)
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy> LogicalSize<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn width(&self, mode: WritingMode) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            self.block
 | 
						||
        } else {
 | 
						||
            self.inline
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn set_width(&mut self, mode: WritingMode, width: T) {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            self.block = width
 | 
						||
        } else {
 | 
						||
            self.inline = width
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn height(&self, mode: WritingMode) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            self.inline
 | 
						||
        } else {
 | 
						||
            self.block
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn set_height(&mut self, mode: WritingMode, height: T) {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            self.inline = height
 | 
						||
        } else {
 | 
						||
            self.block = height
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            Size2D::new(self.block, self.inline)
 | 
						||
        } else {
 | 
						||
            Size2D::new(self.inline, self.block)
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {
 | 
						||
        if mode_from == mode_to {
 | 
						||
            self.debug_writing_mode.check(mode_from);
 | 
						||
            *self
 | 
						||
        } else {
 | 
						||
            LogicalSize::from_physical(mode_to, self.to_physical(mode_from))
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Add<T, Output = T>> Add for LogicalSize<T> {
 | 
						||
    type Output = LogicalSize<T>;
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn add(self, other: LogicalSize<T>) -> LogicalSize<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalSize {
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
            inline: self.inline + other.inline,
 | 
						||
            block: self.block + other.block,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Sub<T, Output = T>> Sub for LogicalSize<T> {
 | 
						||
    type Output = LogicalSize<T>;
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalSize {
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
            inline: self.inline - other.inline,
 | 
						||
            block: self.block - other.block,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/// A 2D point in flow-relative dimensions
 | 
						||
#[derive(Clone, Copy, Eq, PartialEq)]
 | 
						||
#[cfg_attr(feature = "servo", derive(Serialize))]
 | 
						||
pub struct LogicalPoint<T> {
 | 
						||
    /// inline-axis coordinate
 | 
						||
    pub i: T,
 | 
						||
    /// block-axis coordinate
 | 
						||
    pub b: T,
 | 
						||
    debug_writing_mode: DebugWritingMode,
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Debug> Debug for LogicalPoint<T> {
 | 
						||
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
 | 
						||
        write!(
 | 
						||
            formatter,
 | 
						||
            "LogicalPoint({:?} (i{:?}, b{:?}))",
 | 
						||
            self.debug_writing_mode, self.i, self.b
 | 
						||
        )
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
// Can not implement the Zero trait: its zero() method does not have the `mode` parameter.
 | 
						||
impl<T: Zero> LogicalPoint<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn zero(mode: WritingMode) -> LogicalPoint<T> {
 | 
						||
        LogicalPoint {
 | 
						||
            i: Zero::zero(),
 | 
						||
            b: Zero::zero(),
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy> LogicalPoint<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {
 | 
						||
        LogicalPoint {
 | 
						||
            i: i,
 | 
						||
            b: b,
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn from_physical(
 | 
						||
        mode: WritingMode,
 | 
						||
        point: Point2D<T>,
 | 
						||
        container_size: Size2D<T>,
 | 
						||
    ) -> LogicalPoint<T> {
 | 
						||
        if mode.is_vertical() {
 | 
						||
            LogicalPoint {
 | 
						||
                i: if mode.is_inline_tb() {
 | 
						||
                    point.y
 | 
						||
                } else {
 | 
						||
                    container_size.height - point.y
 | 
						||
                },
 | 
						||
                b: if mode.is_vertical_lr() {
 | 
						||
                    point.x
 | 
						||
                } else {
 | 
						||
                    container_size.width - point.x
 | 
						||
                },
 | 
						||
                debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            LogicalPoint {
 | 
						||
                i: if mode.is_bidi_ltr() {
 | 
						||
                    point.x
 | 
						||
                } else {
 | 
						||
                    container_size.width - point.x
 | 
						||
                },
 | 
						||
                b: point.y,
 | 
						||
                debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                self.b
 | 
						||
            } else {
 | 
						||
                container_size.width - self.b
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                self.i
 | 
						||
            } else {
 | 
						||
                container_size.width - self.i
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            self.b = if mode.is_vertical_lr() {
 | 
						||
                x
 | 
						||
            } else {
 | 
						||
                container_size.width - x
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            self.i = if mode.is_bidi_ltr() {
 | 
						||
                x
 | 
						||
            } else {
 | 
						||
                container_size.width - x
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                self.i
 | 
						||
            } else {
 | 
						||
                container_size.height - self.i
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            self.b
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            self.i = if mode.is_inline_tb() {
 | 
						||
                y
 | 
						||
            } else {
 | 
						||
                container_size.height - y
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            self.b = y
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            Point2D::new(
 | 
						||
                if mode.is_vertical_lr() {
 | 
						||
                    self.b
 | 
						||
                } else {
 | 
						||
                    container_size.width - self.b
 | 
						||
                },
 | 
						||
                if mode.is_inline_tb() {
 | 
						||
                    self.i
 | 
						||
                } else {
 | 
						||
                    container_size.height - self.i
 | 
						||
                },
 | 
						||
            )
 | 
						||
        } else {
 | 
						||
            Point2D::new(
 | 
						||
                if mode.is_bidi_ltr() {
 | 
						||
                    self.i
 | 
						||
                } else {
 | 
						||
                    container_size.width - self.i
 | 
						||
                },
 | 
						||
                self.b,
 | 
						||
            )
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn convert(
 | 
						||
        &self,
 | 
						||
        mode_from: WritingMode,
 | 
						||
        mode_to: WritingMode,
 | 
						||
        container_size: Size2D<T>,
 | 
						||
    ) -> LogicalPoint<T> {
 | 
						||
        if mode_from == mode_to {
 | 
						||
            self.debug_writing_mode.check(mode_from);
 | 
						||
            *self
 | 
						||
        } else {
 | 
						||
            LogicalPoint::from_physical(
 | 
						||
                mode_to,
 | 
						||
                self.to_physical(mode_from, container_size),
 | 
						||
                container_size,
 | 
						||
            )
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Add<T, Output = T>> LogicalPoint<T> {
 | 
						||
    /// This doesn’t really makes sense,
 | 
						||
    /// but happens when dealing with multiple origins.
 | 
						||
    #[inline]
 | 
						||
    pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalPoint {
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
            i: self.i + other.i,
 | 
						||
            b: self.b + other.b,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> {
 | 
						||
    type Output = LogicalPoint<T>;
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalPoint {
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
            i: self.i + other.inline,
 | 
						||
            b: self.b + other.block,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> {
 | 
						||
    type Output = LogicalPoint<T>;
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalPoint {
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
            i: self.i - other.inline,
 | 
						||
            b: self.b - other.block,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/// A "margin" in flow-relative dimensions
 | 
						||
/// Represents the four sides of the margins, borders, or padding of a CSS box,
 | 
						||
/// or a combination of those.
 | 
						||
/// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
 | 
						||
#[derive(Clone, Copy, Eq, PartialEq)]
 | 
						||
#[cfg_attr(feature = "servo", derive(Serialize))]
 | 
						||
pub struct LogicalMargin<T> {
 | 
						||
    pub block_start: T,
 | 
						||
    pub inline_end: T,
 | 
						||
    pub block_end: T,
 | 
						||
    pub inline_start: T,
 | 
						||
    debug_writing_mode: DebugWritingMode,
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Debug> Debug for LogicalMargin<T> {
 | 
						||
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
 | 
						||
        let writing_mode_string = if cfg!(debug_assertions) {
 | 
						||
            format!("{:?}, ", self.debug_writing_mode)
 | 
						||
        } else {
 | 
						||
            "".to_owned()
 | 
						||
        };
 | 
						||
 | 
						||
        write!(
 | 
						||
            formatter,
 | 
						||
            "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})",
 | 
						||
            writing_mode_string,
 | 
						||
            self.inline_start,
 | 
						||
            self.inline_end,
 | 
						||
            self.block_start,
 | 
						||
            self.block_end
 | 
						||
        )
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Zero> LogicalMargin<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn zero(mode: WritingMode) -> LogicalMargin<T> {
 | 
						||
        LogicalMargin {
 | 
						||
            block_start: Zero::zero(),
 | 
						||
            inline_end: Zero::zero(),
 | 
						||
            block_end: Zero::zero(),
 | 
						||
            inline_start: Zero::zero(),
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T> LogicalMargin<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn new(
 | 
						||
        mode: WritingMode,
 | 
						||
        block_start: T,
 | 
						||
        inline_end: T,
 | 
						||
        block_end: T,
 | 
						||
        inline_start: T,
 | 
						||
    ) -> LogicalMargin<T> {
 | 
						||
        LogicalMargin {
 | 
						||
            block_start: block_start,
 | 
						||
            inline_end: inline_end,
 | 
						||
            block_end: block_end,
 | 
						||
            inline_start: inline_start,
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {
 | 
						||
        let block_start;
 | 
						||
        let inline_end;
 | 
						||
        let block_end;
 | 
						||
        let inline_start;
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                block_start = offsets.left;
 | 
						||
                block_end = offsets.right;
 | 
						||
            } else {
 | 
						||
                block_start = offsets.right;
 | 
						||
                block_end = offsets.left;
 | 
						||
            }
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                inline_start = offsets.top;
 | 
						||
                inline_end = offsets.bottom;
 | 
						||
            } else {
 | 
						||
                inline_start = offsets.bottom;
 | 
						||
                inline_end = offsets.top;
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            block_start = offsets.top;
 | 
						||
            block_end = offsets.bottom;
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                inline_start = offsets.left;
 | 
						||
                inline_end = offsets.right;
 | 
						||
            } else {
 | 
						||
                inline_start = offsets.right;
 | 
						||
                inline_end = offsets.left;
 | 
						||
            }
 | 
						||
        }
 | 
						||
        LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy> LogicalMargin<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {
 | 
						||
        LogicalMargin::new(mode, value, value, value, value)
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn top(&self, mode: WritingMode) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                self.inline_start
 | 
						||
            } else {
 | 
						||
                self.inline_end
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            self.block_start
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn set_top(&mut self, mode: WritingMode, top: T) {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                self.inline_start = top
 | 
						||
            } else {
 | 
						||
                self.inline_end = top
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            self.block_start = top
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn right(&self, mode: WritingMode) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                self.block_end
 | 
						||
            } else {
 | 
						||
                self.block_start
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                self.inline_end
 | 
						||
            } else {
 | 
						||
                self.inline_start
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn set_right(&mut self, mode: WritingMode, right: T) {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                self.block_end = right
 | 
						||
            } else {
 | 
						||
                self.block_start = right
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                self.inline_end = right
 | 
						||
            } else {
 | 
						||
                self.inline_start = right
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn bottom(&self, mode: WritingMode) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                self.inline_end
 | 
						||
            } else {
 | 
						||
                self.inline_start
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            self.block_end
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                self.inline_end = bottom
 | 
						||
            } else {
 | 
						||
                self.inline_start = bottom
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            self.block_end = bottom
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn left(&self, mode: WritingMode) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                self.block_start
 | 
						||
            } else {
 | 
						||
                self.block_end
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                self.inline_start
 | 
						||
            } else {
 | 
						||
                self.inline_end
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn set_left(&mut self, mode: WritingMode, left: T) {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                self.block_start = left
 | 
						||
            } else {
 | 
						||
                self.block_end = left
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                self.inline_start = left
 | 
						||
            } else {
 | 
						||
                self.inline_end = left
 | 
						||
            }
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        let top;
 | 
						||
        let right;
 | 
						||
        let bottom;
 | 
						||
        let left;
 | 
						||
        if mode.is_vertical() {
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                left = self.block_start;
 | 
						||
                right = self.block_end;
 | 
						||
            } else {
 | 
						||
                right = self.block_start;
 | 
						||
                left = self.block_end;
 | 
						||
            }
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                top = self.inline_start;
 | 
						||
                bottom = self.inline_end;
 | 
						||
            } else {
 | 
						||
                bottom = self.inline_start;
 | 
						||
                top = self.inline_end;
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            top = self.block_start;
 | 
						||
            bottom = self.block_end;
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                left = self.inline_start;
 | 
						||
                right = self.inline_end;
 | 
						||
            } else {
 | 
						||
                right = self.inline_start;
 | 
						||
                left = self.inline_end;
 | 
						||
            }
 | 
						||
        }
 | 
						||
        SideOffsets2D::new(top, right, bottom, left)
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {
 | 
						||
        if mode_from == mode_to {
 | 
						||
            self.debug_writing_mode.check(mode_from);
 | 
						||
            *self
 | 
						||
        } else {
 | 
						||
            LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: PartialEq + Zero> LogicalMargin<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn is_zero(&self) -> bool {
 | 
						||
        self.block_start == Zero::zero() &&
 | 
						||
            self.inline_end == Zero::zero() &&
 | 
						||
            self.block_end == Zero::zero() &&
 | 
						||
            self.inline_start == Zero::zero()
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn inline_start_end(&self) -> T {
 | 
						||
        self.inline_start + self.inline_end
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn block_start_end(&self) -> T {
 | 
						||
        self.block_start + self.block_end
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn start_end(&self, direction: Direction) -> T {
 | 
						||
        match direction {
 | 
						||
            Direction::Inline => self.inline_start + self.inline_end,
 | 
						||
            Direction::Block => self.block_start + self.block_end,
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn top_bottom(&self, mode: WritingMode) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            self.inline_start_end()
 | 
						||
        } else {
 | 
						||
            self.block_start_end()
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn left_right(&self, mode: WritingMode) -> T {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        if mode.is_vertical() {
 | 
						||
            self.block_start_end()
 | 
						||
        } else {
 | 
						||
            self.inline_start_end()
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Add<T, Output = T>> Add for LogicalMargin<T> {
 | 
						||
    type Output = LogicalMargin<T>;
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalMargin {
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
            block_start: self.block_start + other.block_start,
 | 
						||
            inline_end: self.inline_end + other.inline_end,
 | 
						||
            block_end: self.block_end + other.block_end,
 | 
						||
            inline_start: self.inline_start + other.inline_start,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {
 | 
						||
    type Output = LogicalMargin<T>;
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalMargin {
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
            block_start: self.block_start - other.block_start,
 | 
						||
            inline_end: self.inline_end - other.inline_end,
 | 
						||
            block_end: self.block_end - other.block_end,
 | 
						||
            inline_start: self.inline_start - other.inline_start,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
/// A rectangle in flow-relative dimensions
 | 
						||
#[derive(Clone, Copy, Eq, PartialEq)]
 | 
						||
#[cfg_attr(feature = "servo", derive(Serialize))]
 | 
						||
pub struct LogicalRect<T> {
 | 
						||
    pub start: LogicalPoint<T>,
 | 
						||
    pub size: LogicalSize<T>,
 | 
						||
    debug_writing_mode: DebugWritingMode,
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Debug> Debug for LogicalRect<T> {
 | 
						||
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
 | 
						||
        let writing_mode_string = if cfg!(debug_assertions) {
 | 
						||
            format!("{:?}, ", self.debug_writing_mode)
 | 
						||
        } else {
 | 
						||
            "".to_owned()
 | 
						||
        };
 | 
						||
 | 
						||
        write!(
 | 
						||
            formatter,
 | 
						||
            "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))",
 | 
						||
            writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b
 | 
						||
        )
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Zero> LogicalRect<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn zero(mode: WritingMode) -> LogicalRect<T> {
 | 
						||
        LogicalRect {
 | 
						||
            start: LogicalPoint::zero(mode),
 | 
						||
            size: LogicalSize::zero(mode),
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy> LogicalRect<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn new(
 | 
						||
        mode: WritingMode,
 | 
						||
        inline_start: T,
 | 
						||
        block_start: T,
 | 
						||
        inline: T,
 | 
						||
        block: T,
 | 
						||
    ) -> LogicalRect<T> {
 | 
						||
        LogicalRect {
 | 
						||
            start: LogicalPoint::new(mode, inline_start, block_start),
 | 
						||
            size: LogicalSize::new(mode, inline, block),
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn from_point_size(
 | 
						||
        mode: WritingMode,
 | 
						||
        start: LogicalPoint<T>,
 | 
						||
        size: LogicalSize<T>,
 | 
						||
    ) -> LogicalRect<T> {
 | 
						||
        start.debug_writing_mode.check(mode);
 | 
						||
        size.debug_writing_mode.check(mode);
 | 
						||
        LogicalRect {
 | 
						||
            start: start,
 | 
						||
            size: size,
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn from_physical(
 | 
						||
        mode: WritingMode,
 | 
						||
        rect: Rect<T>,
 | 
						||
        container_size: Size2D<T>,
 | 
						||
    ) -> LogicalRect<T> {
 | 
						||
        let inline_start;
 | 
						||
        let block_start;
 | 
						||
        let inline;
 | 
						||
        let block;
 | 
						||
        if mode.is_vertical() {
 | 
						||
            inline = rect.size.height;
 | 
						||
            block = rect.size.width;
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                block_start = rect.origin.x;
 | 
						||
            } else {
 | 
						||
                block_start = container_size.width - (rect.origin.x + rect.size.width);
 | 
						||
            }
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                inline_start = rect.origin.y;
 | 
						||
            } else {
 | 
						||
                inline_start = container_size.height - (rect.origin.y + rect.size.height);
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            inline = rect.size.width;
 | 
						||
            block = rect.size.height;
 | 
						||
            block_start = rect.origin.y;
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                inline_start = rect.origin.x;
 | 
						||
            } else {
 | 
						||
                inline_start = container_size.width - (rect.origin.x + rect.size.width);
 | 
						||
            }
 | 
						||
        }
 | 
						||
        LogicalRect {
 | 
						||
            start: LogicalPoint::new(mode, inline_start, block_start),
 | 
						||
            size: LogicalSize::new(mode, inline, block),
 | 
						||
            debug_writing_mode: DebugWritingMode::new(mode),
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn inline_end(&self) -> T {
 | 
						||
        self.start.i + self.size.inline
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn block_end(&self) -> T {
 | 
						||
        self.start.b + self.size.block
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {
 | 
						||
        self.debug_writing_mode.check(mode);
 | 
						||
        let x;
 | 
						||
        let y;
 | 
						||
        let width;
 | 
						||
        let height;
 | 
						||
        if mode.is_vertical() {
 | 
						||
            width = self.size.block;
 | 
						||
            height = self.size.inline;
 | 
						||
            if mode.is_vertical_lr() {
 | 
						||
                x = self.start.b;
 | 
						||
            } else {
 | 
						||
                x = container_size.width - self.block_end();
 | 
						||
            }
 | 
						||
            if mode.is_inline_tb() {
 | 
						||
                y = self.start.i;
 | 
						||
            } else {
 | 
						||
                y = container_size.height - self.inline_end();
 | 
						||
            }
 | 
						||
        } else {
 | 
						||
            width = self.size.inline;
 | 
						||
            height = self.size.block;
 | 
						||
            y = self.start.b;
 | 
						||
            if mode.is_bidi_ltr() {
 | 
						||
                x = self.start.i;
 | 
						||
            } else {
 | 
						||
                x = container_size.width - self.inline_end();
 | 
						||
            }
 | 
						||
        }
 | 
						||
        Rect {
 | 
						||
            origin: Point2D::new(x, y),
 | 
						||
            size: Size2D::new(width, height),
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    pub fn convert(
 | 
						||
        &self,
 | 
						||
        mode_from: WritingMode,
 | 
						||
        mode_to: WritingMode,
 | 
						||
        container_size: Size2D<T>,
 | 
						||
    ) -> LogicalRect<T> {
 | 
						||
        if mode_from == mode_to {
 | 
						||
            self.debug_writing_mode.check(mode_from);
 | 
						||
            *self
 | 
						||
        } else {
 | 
						||
            LogicalRect::from_physical(
 | 
						||
                mode_to,
 | 
						||
                self.to_physical(mode_from, container_size),
 | 
						||
                container_size,
 | 
						||
            )
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {
 | 
						||
        LogicalRect {
 | 
						||
            start: self.start + offset,
 | 
						||
            ..*self
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {
 | 
						||
        LogicalRect {
 | 
						||
            start: self.start +
 | 
						||
                LogicalSize {
 | 
						||
                    inline: offset.i,
 | 
						||
                    block: offset.b,
 | 
						||
                    debug_writing_mode: offset.debug_writing_mode,
 | 
						||
                },
 | 
						||
            size: self.size,
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {
 | 
						||
    #[inline]
 | 
						||
    pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
 | 
						||
        let inline_start = min(self.start.i, other.start.i);
 | 
						||
        let block_start = min(self.start.b, other.start.b);
 | 
						||
        LogicalRect {
 | 
						||
            start: LogicalPoint {
 | 
						||
                i: inline_start,
 | 
						||
                b: block_start,
 | 
						||
                debug_writing_mode: self.debug_writing_mode,
 | 
						||
            },
 | 
						||
            size: LogicalSize {
 | 
						||
                inline: max(self.inline_end(), other.inline_end()) - inline_start,
 | 
						||
                block: max(self.block_end(), other.block_end()) - block_start,
 | 
						||
                debug_writing_mode: self.debug_writing_mode,
 | 
						||
            },
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {
 | 
						||
    type Output = LogicalRect<T>;
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalRect {
 | 
						||
            start: LogicalPoint {
 | 
						||
                // Growing a rectangle on the start side means pushing its
 | 
						||
                // start point on the negative direction.
 | 
						||
                i: self.start.i - other.inline_start,
 | 
						||
                b: self.start.b - other.block_start,
 | 
						||
                debug_writing_mode: self.debug_writing_mode,
 | 
						||
            },
 | 
						||
            size: LogicalSize {
 | 
						||
                inline: self.size.inline + other.inline_start_end(),
 | 
						||
                block: self.size.block + other.block_start_end(),
 | 
						||
                debug_writing_mode: self.debug_writing_mode,
 | 
						||
            },
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
impl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {
 | 
						||
    type Output = LogicalRect<T>;
 | 
						||
 | 
						||
    #[inline]
 | 
						||
    fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {
 | 
						||
        self.debug_writing_mode
 | 
						||
            .check_debug(other.debug_writing_mode);
 | 
						||
        LogicalRect {
 | 
						||
            start: LogicalPoint {
 | 
						||
                // Shrinking a rectangle on the start side means pushing its
 | 
						||
                // start point on the positive direction.
 | 
						||
                i: self.start.i + other.inline_start,
 | 
						||
                b: self.start.b + other.block_start,
 | 
						||
                debug_writing_mode: self.debug_writing_mode,
 | 
						||
            },
 | 
						||
            size: LogicalSize {
 | 
						||
                inline: self.size.inline - other.inline_start_end(),
 | 
						||
                block: self.size.block - other.block_start_end(),
 | 
						||
                debug_writing_mode: self.debug_writing_mode,
 | 
						||
            },
 | 
						||
            debug_writing_mode: self.debug_writing_mode,
 | 
						||
        }
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
#[derive(Clone, Copy, Debug, PartialEq)]
 | 
						||
pub enum PhysicalSide {
 | 
						||
    Top,
 | 
						||
    Right,
 | 
						||
    Bottom,
 | 
						||
    Left,
 | 
						||
}
 | 
						||
 | 
						||
#[derive(Clone, Copy, Debug, PartialEq)]
 | 
						||
pub enum PhysicalCorner {
 | 
						||
    TopLeft,
 | 
						||
    TopRight,
 | 
						||
    BottomRight,
 | 
						||
    BottomLeft,
 | 
						||
}
 |