--- a/servo/components/style/values/specified/align.rs
+++ b/servo/components/style/values/specified/align.rs
@@ -5,23 +5,23 @@
//! 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::{self, Write};
-use style_traits::{CssWriter, ParseError, ToCss};
+use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, ToCss};
bitflags! {
/// Constants shared by multiple CSS Box Alignment properties
///
/// These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
- #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
+ #[derive(MallocSizeOf, ToComputedValue)]
pub struct AlignFlags: u8 {
// Enumeration stored in the lower 5 bits:
/// 'auto'
const AUTO = structs::NS_STYLE_ALIGN_AUTO as u8;
/// 'normal'
const NORMAL = structs::NS_STYLE_ALIGN_NORMAL as u8;
/// 'start'
const START = structs::NS_STYLE_ALIGN_START as u8;
@@ -130,18 +130,17 @@ pub enum AxisDirection {
Block,
/// Inline direction.
Inline,
}
/// Shared value for the `align-content` and `justify-content` properties.
///
/// <https://drafts.csswg.org/css-align/#content-distribution>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct ContentDistribution {
primary: AlignFlags,
// FIXME(https://github.com/w3c/csswg-drafts/issues/1002): This will need to
// accept fallback alignment, eventually.
}
impl ContentDistribution {
@@ -187,16 +186,19 @@ impl ContentDistribution {
self.primary
}
/// Parse a value for align-content / justify-content.
pub fn parse<'i, 't>(
input: &mut Parser<'i, 't>,
axis: AxisDirection,
) -> Result<Self, ParseError<'i>> {
+ // NOTE Please also update the `list_keywords` function below
+ // when this function is updated.
+
// Try to parse normal first
if input.try(|i| i.expect_ident_matching("normal")).is_ok() {
return Ok(ContentDistribution::normal());
}
// Parse <baseline-position>, but only on the block axis.
if axis == AxisDirection::Block {
if let Ok(value) = input.try(parse_baseline) {
@@ -223,37 +225,57 @@ impl ContentDistribution {
"left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
"right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
};
Ok(ContentDistribution::new(
content_position | overflow_position,
))
}
+
+ fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
+ f(&["normal"]);
+ if axis == AxisDirection::Block {
+ list_baseline_keywords(f);
+ }
+ list_content_distribution_keywords(f);
+ list_overflow_position_keywords(f);
+ f(&["start", "end", "flex-start", "flex-end", "center"]);
+ if axis == AxisDirection::Inline {
+ f(&["left", "right"]);
+ }
+ }
}
/// Value for the `align-content` property.
///
/// <https://drafts.csswg.org/css-align/#propdef-align-content>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct AlignContent(pub ContentDistribution);
impl Parse for AlignContent {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
+ // NOTE Please also update `impl SpecifiedValueInfo` below when
+ // this function is updated.
Ok(AlignContent(ContentDistribution::parse(
input,
AxisDirection::Block,
)?))
}
}
+impl SpecifiedValueInfo for AlignContent {
+ fn collect_completion_keywords(f: KeywordsCollectFn) {
+ ContentDistribution::list_keywords(f, AxisDirection::Block);
+ }
+}
+
#[cfg(feature = "gecko")]
impl From<u16> for AlignContent {
fn from(bits: u16) -> Self {
AlignContent(ContentDistribution::from_bits(bits))
}
}
#[cfg(feature = "gecko")]
@@ -261,49 +283,55 @@ impl From<AlignContent> for u16 {
fn from(v: AlignContent) -> u16 {
v.0.as_bits()
}
}
/// Value for the `justify-content` property.
///
/// <https://drafts.csswg.org/css-align/#propdef-align-content>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct JustifyContent(pub ContentDistribution);
impl Parse for JustifyContent {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
+ // NOTE Please also update `impl SpecifiedValueInfo` below when
+ // this function is updated.
Ok(JustifyContent(ContentDistribution::parse(
input,
AxisDirection::Inline,
)?))
}
}
+impl SpecifiedValueInfo for JustifyContent {
+ fn collect_completion_keywords(f: KeywordsCollectFn) {
+ ContentDistribution::list_keywords(f, AxisDirection::Inline);
+ }
+}
+
#[cfg(feature = "gecko")]
impl From<u16> for JustifyContent {
fn from(bits: u16) -> Self {
JustifyContent(ContentDistribution::from_bits(bits))
}
}
#[cfg(feature = "gecko")]
impl From<JustifyContent> for u16 {
fn from(v: JustifyContent) -> u16 {
v.0.as_bits()
}
}
/// <https://drafts.csswg.org/css-align/#self-alignment>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct SelfAlignment(pub AlignFlags);
impl SelfAlignment {
/// The initial value 'auto'
#[inline]
pub fn auto() -> Self {
SelfAlignment(AlignFlags::AUTO)
}
@@ -318,16 +346,19 @@ impl SelfAlignment {
}
}
/// Parse a self-alignment value on one of the axis.
pub fn parse<'i, 't>(
input: &mut Parser<'i, 't>,
axis: AxisDirection,
) -> Result<Self, ParseError<'i>> {
+ // NOTE Please also update the `list_keywords` function below
+ // when this function is updated.
+
// <baseline-position>
//
// It's weird that this accepts <baseline-position>, but not
// justify-content...
if let Ok(value) = input.try(parse_baseline) {
return Ok(SelfAlignment(value));
}
@@ -338,85 +369,105 @@ impl SelfAlignment {
// <overflow-position>? <self-position>
let overflow_position = input
.try(parse_overflow_position)
.unwrap_or(AlignFlags::empty());
let self_position = parse_self_position(input, axis)?;
Ok(SelfAlignment(overflow_position | self_position))
}
+
+ fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
+ list_baseline_keywords(f);
+ list_auto_normal_stretch(f);
+ list_overflow_position_keywords(f);
+ list_self_position_keywords(f, axis);
+ }
}
/// The specified value of the align-self property.
///
/// <https://drafts.csswg.org/css-align/#propdef-align-self>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct AlignSelf(pub SelfAlignment);
impl Parse for AlignSelf {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
+ // NOTE Please also update `impl SpecifiedValueInfo` below when
+ // this function is updated.
Ok(AlignSelf(SelfAlignment::parse(
input,
AxisDirection::Block,
)?))
}
}
+impl SpecifiedValueInfo for AlignSelf {
+ fn collect_completion_keywords(f: KeywordsCollectFn) {
+ SelfAlignment::list_keywords(f, AxisDirection::Block);
+ }
+}
+
impl From<u8> for AlignSelf {
fn from(bits: u8) -> Self {
AlignSelf(SelfAlignment(AlignFlags::from_bits_truncate(bits)))
}
}
impl From<AlignSelf> for u8 {
fn from(align: AlignSelf) -> u8 {
(align.0).0.bits()
}
}
/// The specified value of the justify-self property.
///
/// <https://drafts.csswg.org/css-align/#propdef-justify-self>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct JustifySelf(pub SelfAlignment);
impl Parse for JustifySelf {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
+ // NOTE Please also update `impl SpecifiedValueInfo` below when
+ // this function is updated.
Ok(JustifySelf(SelfAlignment::parse(
input,
AxisDirection::Inline,
)?))
}
}
+impl SpecifiedValueInfo for JustifySelf {
+ fn collect_completion_keywords(f: KeywordsCollectFn) {
+ SelfAlignment::list_keywords(f, AxisDirection::Inline);
+ }
+}
+
impl From<u8> for JustifySelf {
fn from(bits: u8) -> Self {
JustifySelf(SelfAlignment(AlignFlags::from_bits_truncate(bits)))
}
}
impl From<JustifySelf> for u8 {
fn from(justify: JustifySelf) -> u8 {
(justify.0).0.bits()
}
}
/// Value of the `align-items` property
///
/// <https://drafts.csswg.org/css-align/#self-alignment>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToComputedValue, ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct AlignItems(pub AlignFlags);
impl AlignItems {
/// The initial value 'normal'
#[inline]
pub fn normal() -> Self {
AlignItems(AlignFlags::NORMAL)
}
@@ -424,16 +475,19 @@ impl AlignItems {
impl Parse for AlignItems {
// normal | stretch | <baseline-position> |
// <overflow-position>? <self-position>
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
+ // NOTE Please also update `impl SpecifiedValueInfo` below when
+ // this function is updated.
+
// <baseline-position>
if let Ok(baseline) = input.try(parse_baseline) {
return Ok(AlignItems(baseline));
}
// normal | stretch
if let Ok(value) = input.try(parse_normal_stretch) {
return Ok(AlignItems(value));
@@ -442,21 +496,29 @@ impl Parse for AlignItems {
let overflow = input
.try(parse_overflow_position)
.unwrap_or(AlignFlags::empty());
let self_position = parse_self_position(input, AxisDirection::Block)?;
Ok(AlignItems(self_position | overflow))
}
}
+impl SpecifiedValueInfo for AlignItems {
+ fn collect_completion_keywords(f: KeywordsCollectFn) {
+ list_baseline_keywords(f);
+ list_normal_stretch(f);
+ list_overflow_position_keywords(f);
+ list_self_position_keywords(f, AxisDirection::Block);
+ }
+}
+
/// Value of the `justify-items` property
///
/// <https://drafts.csswg.org/css-align/#justify-items-property>
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToCss)]
+#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
pub struct JustifyItems(pub AlignFlags);
impl JustifyItems {
/// The initial value 'legacy'
#[inline]
pub fn legacy() -> Self {
JustifyItems(AlignFlags::LEGACY)
}
@@ -468,16 +530,19 @@ impl JustifyItems {
}
}
impl Parse for JustifyItems {
fn parse<'i, 't>(
_: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
+ // NOTE Please also update `impl SpecifiedValueInfo` below when
+ // this function is updated.
+
// <baseline-position>
//
// It's weird that this accepts <baseline-position>, but not
// justify-content...
if let Ok(baseline) = input.try(parse_baseline) {
return Ok(JustifyItems(baseline));
}
@@ -495,109 +560,169 @@ impl Parse for JustifyItems {
let overflow = input
.try(parse_overflow_position)
.unwrap_or(AlignFlags::empty());
let self_position = parse_self_position(input, AxisDirection::Inline)?;
Ok(JustifyItems(overflow | self_position))
}
}
+impl SpecifiedValueInfo for JustifyItems {
+ fn collect_completion_keywords(f: KeywordsCollectFn) {
+ list_baseline_keywords(f);
+ list_normal_stretch(f);
+ list_legacy_keywords(f);
+ list_overflow_position_keywords(f);
+ list_self_position_keywords(f, AxisDirection::Inline);
+ }
+}
+
// auto | normal | stretch
fn parse_auto_normal_stretch<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<AlignFlags, ParseError<'i>> {
+ // NOTE Please also update the `list_auto_normal_stretch` function
+ // below when this function is updated.
try_match_ident_ignore_ascii_case! { input,
"auto" => Ok(AlignFlags::AUTO),
"normal" => Ok(AlignFlags::NORMAL),
"stretch" => Ok(AlignFlags::STRETCH),
}
}
+fn list_auto_normal_stretch(f: KeywordsCollectFn) {
+ f(&["auto", "normal", "stretch"]);
+}
+
// normal | stretch
fn parse_normal_stretch<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
+ // NOTE Please also update the `list_normal_stretch` function below
+ // when this function is updated.
try_match_ident_ignore_ascii_case! { input,
"normal" => Ok(AlignFlags::NORMAL),
"stretch" => Ok(AlignFlags::STRETCH),
}
}
+fn list_normal_stretch(f: KeywordsCollectFn) {
+ f(&["normal", "stretch"]);
+}
+
// <baseline-position>
fn parse_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
+ // NOTE Please also update the `list_baseline_keywords` function
+ // below when this function is updated.
try_match_ident_ignore_ascii_case! { input,
"baseline" => Ok(AlignFlags::BASELINE),
"first" => {
input.expect_ident_matching("baseline")?;
Ok(AlignFlags::BASELINE)
}
"last" => {
input.expect_ident_matching("baseline")?;
Ok(AlignFlags::LAST_BASELINE)
}
}
}
+fn list_baseline_keywords(f: KeywordsCollectFn) {
+ f(&["baseline", "first baseline", "last baseline"]);
+}
+
// <content-distribution>
fn parse_content_distribution<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<AlignFlags, ParseError<'i>> {
+ // NOTE Please also update the `list_content_distribution_keywords`
+ // function below when this function is updated.
try_match_ident_ignore_ascii_case! { input,
"stretch" => Ok(AlignFlags::STRETCH),
"space-between" => Ok(AlignFlags::SPACE_BETWEEN),
"space-around" => Ok(AlignFlags::SPACE_AROUND),
"space-evenly" => Ok(AlignFlags::SPACE_EVENLY),
}
}
+fn list_content_distribution_keywords(f: KeywordsCollectFn) {
+ f(&["stretch", "space-between", "space-around", "space-evenly"]);
+}
+
// <overflow-position>
fn parse_overflow_position<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<AlignFlags, ParseError<'i>> {
+ // NOTE Please also update the `list_overflow_position_keywords`
+ // function below when this function is updated.
try_match_ident_ignore_ascii_case! { input,
"safe" => Ok(AlignFlags::SAFE),
"unsafe" => Ok(AlignFlags::UNSAFE),
}
}
+fn list_overflow_position_keywords(f: KeywordsCollectFn) {
+ f(&["safe", "unsafe"]);
+}
+
// <self-position> | left | right in the inline axis.
fn parse_self_position<'i, 't>(
input: &mut Parser<'i, 't>,
axis: AxisDirection,
) -> Result<AlignFlags, ParseError<'i>> {
+ // NOTE Please also update the `list_self_position_keywords`
+ // function below when this function is updated.
Ok(try_match_ident_ignore_ascii_case! { input,
"start" => AlignFlags::START,
"end" => AlignFlags::END,
"flex-start" => AlignFlags::FLEX_START,
"flex-end" => AlignFlags::FLEX_END,
"center" => AlignFlags::CENTER,
"self-start" => AlignFlags::SELF_START,
"self-end" => AlignFlags::SELF_END,
"left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
"right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
})
}
+fn list_self_position_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
+ f(&[
+ "start", "end", "flex-start", "flex-end",
+ "center", "self-start", "self-end",
+ ]);
+ if axis == AxisDirection::Inline {
+ f(&["left", "right"]);
+ }
+}
+
fn parse_left_right_center<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<AlignFlags, ParseError<'i>> {
+ // NOTE Please also update the `list_legacy_keywords` function below
+ // when this function is updated.
Ok(try_match_ident_ignore_ascii_case! { input,
"left" => AlignFlags::LEFT,
"right" => AlignFlags::RIGHT,
"center" => AlignFlags::CENTER,
})
}
// legacy | [ legacy && [ left | right | center ] ]
fn parse_legacy<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
+ // NOTE Please also update the `list_legacy_keywords` function below
+ // when this function is updated.
let flags = try_match_ident_ignore_ascii_case! { input,
"legacy" => {
let flags = input.try(parse_left_right_center)
.unwrap_or(AlignFlags::empty());
return Ok(AlignFlags::LEGACY | flags)
}
"left" => AlignFlags::LEFT,
"right" => AlignFlags::RIGHT,
"center" => AlignFlags::CENTER,
};
input.expect_ident_matching("legacy")?;
Ok(AlignFlags::LEGACY | flags)
}
+
+fn list_legacy_keywords(f: KeywordsCollectFn) {
+ f(&["legacy", "left", "right", "center"]);
+}