From 370aca412d971bb66ae866e6a4fedda2bf586ae9 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 14 Feb 2017 09:13:13 -0800 Subject: [PATCH] servo: Merge #15533 - Stylo: Implement align-content and justify-content (from mbrubeck:align); r=Manishearth This implements parsing and serialization of `align-content` and `justify-content` for Stylo. The implementation should match Gecko exactly (which means it's not exactly up-to-date with the latest draft spec). This is a Stylo-only change; it leaves the current Servo code (which matches an older spec) unchanged for now. r? @Manishearth - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix part of #15001 (github issue number if applicable). - [x] These changes do not require tests because they are stylo-only Source-Repo: https://github.com/servo/servo Source-Revision: 3b72a1f6d2b6b9676682e8fcabbcf66032531eac --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 16fb2db93df696e71f297149a17dc5390bcc3514 --- .../components/style/properties/gecko.mako.rs | 16 +- .../properties/longhand/position.mako.rs | 42 ++- servo/components/style/values/computed/mod.rs | 4 + .../style/values/specified/align.rs | 266 ++++++++++++++++++ .../components/style/values/specified/mod.rs | 4 + 5 files changed, 316 insertions(+), 16 deletions(-) create mode 100644 servo/components/style/values/specified/align.rs diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 5d79857be938..7e702f913e31 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -867,8 +867,8 @@ fn static_assert() { <% skip_position_longhands = " ".join(x.ident for x in SIDES + GRID_LINES) %> <%self:impl_trait style_struct_name="Position" - skip_longhands="${skip_position_longhands} z-index box-sizing order"> - + skip_longhands="${skip_position_longhands} z-index box-sizing order align-content + justify-content"> % for side in SIDES: <% impl_split_style_coord("%s" % side.ident, "mOffset", @@ -906,6 +906,18 @@ fn static_assert() { } } + pub fn set_align_content(&mut self, v: longhands::align_content::computed_value::T) { + self.gecko.mAlignContent = v.bits() + } + + ${impl_simple_copy('align_content', 'mAlignContent')} + + pub fn set_justify_content(&mut self, v: longhands::justify_content::computed_value::T) { + self.gecko.mJustifyContent = v.bits() + } + + ${impl_simple_copy('justify_content', 'mJustifyContent')} + pub fn set_box_sizing(&mut self, v: longhands::box_sizing::computed_value::T) { use computed_values::box_sizing::T; use gecko_bindings::structs::StyleBoxSizing; diff --git a/servo/components/style/properties/longhand/position.mako.rs b/servo/components/style/properties/longhand/position.mako.rs index e4ed6a6efa7d..59edcd926729 100644 --- a/servo/components/style/properties/longhand/position.mako.rs +++ b/servo/components/style/properties/longhand/position.mako.rs @@ -84,14 +84,20 @@ ${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse", spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property", extra_prefixes="webkit", animatable=False)} -// FIXME(stshine): The type of 'justify-content' and 'align-content' is uint16_t in gecko -// FIXME(stshine): Its higher bytes are used to store fallback value. Disable them in geckolib for now -${helpers.single_keyword("justify-content", "flex-start flex-end center space-between space-around", - gecko_constant_prefix="NS_STYLE_JUSTIFY", - products="servo", - extra_prefixes="webkit", - spec="https://drafts.csswg.org/css-flexbox/#justify-content-property", - animatable=False)} +% if product == "servo": + // FIXME: Update Servo to support the same Syntax as Gecko. + ${helpers.single_keyword("justify-content", "stretch flex-start flex-end center space-between space-around", + extra_prefixes="webkit", + spec="https://drafts.csswg.org/css-flexbox/#justify-content-property", + animatable=False)} +% else: + ${helpers.predefined_type(name="justify-content", + type="AlignJustifyContent", + initial_value="specified::AlignJustifyContent::auto()", + spec="https://drafts.csswg.org/css-flexbox/#justify-content-property", + extra_prefixes="webkit", + animatable=False)} +% endif // https://drafts.csswg.org/css-flexbox/#propdef-align-items // FIXME: This is a workaround for 'normal' value. We don't support the Gecko initial value 'normal' yet. @@ -103,12 +109,20 @@ ${helpers.single_keyword("align-items", "stretch flex-start flex-end center base spec="https://drafts.csswg.org/css-flexbox/#align-items-property", animatable=False)} -${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around", - gecko_constant_prefix="NS_STYLE_ALIGN", - products="servo", - extra_prefixes="webkit", - spec="https://drafts.csswg.org/css-flexbox/#align-content-property", - animatable=False)} +% if product == "servo": + // FIXME: Update Servo to support the same Syntax as Gecko. + ${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around", + extra_prefixes="webkit", + spec="https://drafts.csswg.org/css-flexbox/#align-content-property", + animatable=False)} +% else: + ${helpers.predefined_type(name="align-content", + type="AlignJustifyContent", + initial_value="specified::AlignJustifyContent::auto()", + spec="https://drafts.csswg.org/css-flexbox/#align-content-property", + extra_prefixes="webkit", + animatable=False)} +% endif // Flex item properties ${helpers.predefined_type("flex-grow", "Number", diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs index 36b3b2dc6ac8..b648e50c3c55 100644 --- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -16,6 +16,8 @@ pub use cssparser::Color as CSSColor; pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientKind, Image}; pub use self::image::{LengthOrKeyword, LengthOrPercentageOrKeyword}; pub use super::{Auto, Either, None_}; +#[cfg(feature = "gecko")] +pub use super::specified::AlignJustifyContent; pub use super::specified::{Angle, BorderStyle, GridLine, Time, UrlOrNone}; pub use super::specified::url::UrlExtraData; pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; @@ -120,6 +122,8 @@ impl ToComputedValue for specified::CSSColor { } } +#[cfg(feature = "gecko")] +impl ComputedValueAsSpecified for specified::AlignJustifyContent {} impl ComputedValueAsSpecified for specified::BorderStyle {} #[derive(Debug, PartialEq, Clone, Copy)] diff --git a/servo/components/style/values/specified/align.rs b/servo/components/style/values/specified/align.rs new file mode 100644 index 000000000000..fe7c779552b0 --- /dev/null +++ b/servo/components/style/values/specified/align.rs @@ -0,0 +1,266 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +//! Values for CSS Box Alignment properties +//! +//! https://drafts.csswg.org/css-align/ + +use cssparser::Parser; +use gecko_bindings::structs; +use parser::{Parse, ParserContext}; +use std::fmt; +use style_traits::ToCss; +use values::HasViewportPercentage; + +bitflags! { + /// Constants shared by multiple CSS Box Alignment properties + /// + /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants. + pub flags AlignFlags: u8 { + // Enumeration stored in the lower 5 bits: + /// 'auto' + const ALIGN_AUTO = structs::NS_STYLE_ALIGN_AUTO as u8, + /// 'normal' + const ALIGN_NORMAL = structs::NS_STYLE_ALIGN_NORMAL as u8, + /// 'start' + const ALIGN_START = structs::NS_STYLE_ALIGN_START as u8, + /// 'end' + const ALIGN_END = structs::NS_STYLE_ALIGN_END as u8, + /// 'flex-start' + const ALIGN_FLEX_START = structs::NS_STYLE_ALIGN_FLEX_START as u8, + /// 'flex-end' + const ALIGN_FLEX_END = structs::NS_STYLE_ALIGN_FLEX_END as u8, + /// 'center' + const ALIGN_CENTER = structs::NS_STYLE_ALIGN_CENTER as u8, + /// 'left' + const ALIGN_LEFT = structs::NS_STYLE_ALIGN_LEFT as u8, + /// 'left' + const ALIGN_RIGHT = structs::NS_STYLE_ALIGN_RIGHT as u8, + /// 'right' + const ALIGN_BASELINE = structs::NS_STYLE_ALIGN_BASELINE as u8, + /// 'baseline' + const ALIGN_LAST_BASELINE = structs::NS_STYLE_ALIGN_LAST_BASELINE as u8, + /// 'stretch' + const ALIGN_STRETCH = structs::NS_STYLE_ALIGN_STRETCH as u8, + /// 'self-start' + const ALIGN_SELF_START = structs::NS_STYLE_ALIGN_SELF_START as u8, + /// 'self-end' + const ALIGN_SELF_END = structs::NS_STYLE_ALIGN_SELF_END as u8, + /// 'space-between' + const ALIGN_SPACE_BETWEEN = structs::NS_STYLE_ALIGN_SPACE_BETWEEN as u8, + /// 'space-around' + const ALIGN_SPACE_AROUND = structs::NS_STYLE_ALIGN_SPACE_AROUND as u8, + /// 'space-evenly' + const ALIGN_SPACE_EVENLY = structs::NS_STYLE_ALIGN_SPACE_EVENLY as u8, + + // Additional flags stored in the upper bits: + /// 'legacy' (mutually exclusive w. SAFE & UNSAFE) + const ALIGN_LEGACY = structs::NS_STYLE_ALIGN_LEGACY as u8, + /// 'safe' + const ALIGN_SAFE = structs::NS_STYLE_ALIGN_SAFE as u8, + /// 'unsafe' (mutually exclusive w. SAFE) + const ALIGN_UNSAFE = structs::NS_STYLE_ALIGN_UNSAFE as u8, + + /// Mask for the additional flags above. + const ALIGN_FLAG_BITS = structs::NS_STYLE_ALIGN_FLAG_BITS as u8, + } +} + +impl ToCss for AlignFlags { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let s = match *self & !ALIGN_FLAG_BITS { + ALIGN_AUTO => "auto", + ALIGN_NORMAL => "normal", + ALIGN_START => "start", + ALIGN_END => "end", + ALIGN_FLEX_START => "flex-start", + ALIGN_FLEX_END => "flex-end", + ALIGN_CENTER => "center", + ALIGN_LEFT => "left", + ALIGN_RIGHT => "left", + ALIGN_BASELINE => "right", + ALIGN_LAST_BASELINE => "baseline", + ALIGN_STRETCH => "stretch", + ALIGN_SELF_START => "self-start", + ALIGN_SELF_END => "self-end", + ALIGN_SPACE_BETWEEN => "space-between", + ALIGN_SPACE_AROUND => "space-around", + ALIGN_SPACE_EVENLY => "space-evenly", + _ => unreachable!() + }; + dest.write_str(s)?; + + match *self & ALIGN_FLAG_BITS { + ALIGN_LEGACY => { dest.write_str(" legacy")?; } + ALIGN_SAFE => { dest.write_str(" safe")?; } + ALIGN_UNSAFE => { dest.write_str(" unsafe")?; } + _ => {} + } + Ok(()) + } +} + +/// Mask for a single AlignFlags value. +const ALIGN_ALL_BITS: u16 = structs::NS_STYLE_ALIGN_ALL_BITS as u16; +/// Number of bits to shift a fallback alignment. +const ALIGN_ALL_SHIFT: u32 = structs::NS_STYLE_ALIGN_ALL_SHIFT; + +/// Value of the `align-content` or `justify-content` property. +/// +/// https://drafts.csswg.org/css-align/#content-distribution +/// +/// The 16-bit field stores the primary value in its lower 8 bits, and the optional fallback value +/// in its upper 8 bits. This matches the representation of these properties in Gecko. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] +pub struct AlignJustifyContent(u16); + +impl AlignJustifyContent { + /// The initial value 'auto' + #[inline] + pub fn auto() -> Self { + Self::new(ALIGN_AUTO) + } + + /// Construct a value with no fallback. + #[inline] + pub fn new(flags: AlignFlags) -> Self { + AlignJustifyContent(flags.bits() as u16) + } + + /// Construct a value including a fallback alignment. + /// + /// https://drafts.csswg.org/css-align/#fallback-alignment + #[inline] + pub fn with_fallback(flags: AlignFlags, fallback: AlignFlags) -> Self { + AlignJustifyContent(flags.bits() as u16 | ((fallback.bits() as u16) << ALIGN_ALL_SHIFT)) + } + + /// The combined 16-bit flags, for copying into a Gecko style struct. + #[inline] + pub fn bits(self) -> u16 { self.0 } + + /// The primary alignment + #[inline] + pub fn primary(self) -> AlignFlags { + AlignFlags::from_bits((self.0 & ALIGN_ALL_BITS) as u8) + .expect("AlignJustifyContent must contain valid flags") + } + + /// The fallback alignment + #[inline] + pub fn fallback(self) -> AlignFlags { + AlignFlags::from_bits((self.0 >> ALIGN_ALL_SHIFT) as u8) + .expect("AlignJustifyContent must contain valid flags") + } +} + +impl ToCss for AlignJustifyContent { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.primary().to_css(dest)?; + match self.fallback() { + ALIGN_AUTO => {} + fallback => { + dest.write_str(" ")?; + fallback.to_css(dest)?; + } + } + Ok(()) + } +} + +no_viewport_percentage!(AlignJustifyContent); + +impl Parse for AlignJustifyContent { + // normal | | + // [ || [ ? && ] ] + fn parse(_: &ParserContext, input: &mut Parser) -> Result { + // normal | + if let Ok(value) = input.try(|input| parse_normal_or_baseline(input)) { + return Ok(AlignJustifyContent::new(value)) + } + + // followed by optional <*-position> + if let Ok(value) = input.try(|input| parse_content_distribution(input)) { + if let Ok(fallback) = input.try(|input| parse_overflow_content_position(input)) { + return Ok(AlignJustifyContent::with_fallback(value, fallback)) + } + return Ok(AlignJustifyContent::new(value)) + } + + // <*-position> followed by optional + if let Ok(fallback) = input.try(|input| parse_overflow_content_position(input)) { + if let Ok(value) = input.try(|input| parse_content_distribution(input)) { + return Ok(AlignJustifyContent::with_fallback(value, fallback)) + } + return Ok(AlignJustifyContent::new(fallback)) + } + Err(()) + } +} + +// normal | +fn parse_normal_or_baseline(input: &mut Parser) -> Result { + let ident = input.expect_ident()?; + match_ignore_ascii_case! { ident, + "normal" => Ok(ALIGN_NORMAL), + "baseline" => Ok(ALIGN_BASELINE), + _ => Err(()) + } +} + +// +fn parse_content_distribution(input: &mut Parser) -> Result { + let ident = input.expect_ident()?; + match_ignore_ascii_case! { ident, + "stretch" => Ok(ALIGN_STRETCH), + "space_between" => Ok(ALIGN_SPACE_BETWEEN), + "space_around" => Ok(ALIGN_SPACE_AROUND), + "space_evenly" => Ok(ALIGN_SPACE_EVENLY), + _ => Err(()) + } +} + +// [ ? && ] +fn parse_overflow_content_position(input: &mut Parser) -> Result { + // followed by optional + if let Ok(mut content) = input.try(|input| parse_content_position(input)) { + if let Ok(overflow) = input.try(|input| parse_overflow_position(input)) { + content |= overflow; + } + return Ok(content) + } + // followed by required + if let Ok(overflow) = input.try(|input| parse_overflow_position(input)) { + if let Ok(content) = input.try(|input| parse_content_position(input)) { + return Ok(overflow | content) + } + } + return Err(()) +} + +// +fn parse_content_position(input: &mut Parser) -> Result { + let ident = input.expect_ident()?; + match_ignore_ascii_case! { ident, + "start" => Ok(ALIGN_START), + "end" => Ok(ALIGN_END), + "flex-start" => Ok(ALIGN_FLEX_START), + "flex-end" => Ok(ALIGN_FLEX_END), + "center" => Ok(ALIGN_CENTER), + "left" => Ok(ALIGN_LEFT), + "right" => Ok(ALIGN_RIGHT), + _ => Err(()) + } +} + +// +fn parse_overflow_position(input: &mut Parser) -> Result { + let ident = input.expect_ident()?; + match_ignore_ascii_case! { ident, + "safe" => Ok(ALIGN_SAFE), + "unsafe" => Ok(ALIGN_UNSAFE), + _ => Err(()) + } +} diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 8e3315f0ea66..56c038d4e0ed 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -20,6 +20,8 @@ use super::{Auto, CSSFloat, HasViewportPercentage, Either, None_}; use super::computed::{ComputedValueAsSpecified, Context, ToComputedValue}; use super::computed::Shadow as ComputedShadow; +#[cfg(feature = "gecko")] +pub use self::align::AlignJustifyContent; pub use self::grid::GridLine; pub use self::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient}; pub use self::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword}; @@ -29,6 +31,8 @@ pub use self::length::{Percentage, LengthOrNone, LengthOrNumber, LengthOrPercent pub use self::length::{LengthOrPercentageOrNone, LengthOrPercentageOrAutoOrContent, NoCalcLength, CalcUnit}; pub use self::position::{HorizontalPosition, Position, VerticalPosition}; +#[cfg(feature = "gecko")] +pub mod align; pub mod basic_shape; pub mod grid; pub mod image;