Bug 1434130 part 12 - Manually implement collect_completion_keywords for some types. r?emilio draft
authorXidorn Quan <me@upsuper.org>
Thu, 26 Apr 2018 21:04:33 +1000
changeset 788778 b638793227b9f6317b540670227d68ad90a76173
parent 788777 2af95ecd7e1f48bf54a93573a68a9d3f29393e42
child 788779 bab732c8c531cfca1bcd233f769c25bb2e373773
push id108088
push userxquan@mozilla.com
push dateFri, 27 Apr 2018 00:40:56 +0000
reviewersemilio
bugs1434130
milestone61.0a1
Bug 1434130 part 12 - Manually implement collect_completion_keywords for some types. r?emilio MozReview-Commit-ID: 6T35uylxgho
servo/components/style/gecko/url.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/values/generics/font.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/specified/align.rs
servo/components/style/values/specified/image.rs
servo/components/style_traits/cursor.rs
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -10,21 +10,21 @@ use gecko_bindings::structs::{ServoBundl
 use gecko_bindings::structs::mozilla::css::URLValueData;
 use gecko_bindings::structs::root::{RustString, nsStyleImageRequest};
 use gecko_bindings::structs::root::mozilla::css::{ImageValue, URLValue};
 use gecko_bindings::sugar::refptr::RefPtr;
 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
 use parser::{Parse, ParserContext};
 use servo_arc::{Arc, RawOffsetArc};
 use std::mem;
-use style_traits::{ParseError, SpecifiedValueInfo};
+use style_traits::ParseError;
 
 /// A CSS url() value for gecko.
 #[css(function = "url")]
-#[derive(Clone, Debug, PartialEq, ToCss)]
+#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
 pub struct CssUrl {
     /// The URL in unresolved string form.
     ///
     /// Refcounted since cloning this should be cheap and data: uris can be
     /// really large.
     serialization: Arc<String>,
 
     /// The URL extra data.
@@ -115,18 +115,16 @@ impl MallocSizeOf for CssUrl {
 
         // We ignore `extra_data`, because RefPtr is tricky, and there aren't
         // many of them in practise (sharing is common).
 
         0
     }
 }
 
-impl SpecifiedValueInfo for CssUrl {}
-
 /// A specified url() value for general usage.
 #[derive(Clone, Debug, SpecifiedValueInfo, ToComputedValue, ToCss)]
 pub struct SpecifiedUrl {
     /// The specified url value.
     pub url: CssUrl,
     /// Gecko's URLValue so that we can reuse it while rematching a
     /// property with this specified value.
     #[css(skip)]
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -22,17 +22,17 @@ use properties::longhands::font_weight::
 use properties::longhands::visibility::computed_value::T as Visibility;
 use properties::PropertyId;
 use properties::{LonghandId, ShorthandId};
 use servo_arc::Arc;
 use smallvec::SmallVec;
 use std::{cmp, ptr};
 use std::mem::{self, ManuallyDrop};
 #[cfg(feature = "gecko")] use hash::FnvHashMap;
-use style_traits::{ParseError, SpecifiedValueInfo};
+use style_traits::{KeywordsCollectFn, ParseError, SpecifiedValueInfo};
 use super::ComputedValues;
 use values::{CSSFloat, CustomIdent, Either};
 use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
 use values::animated::color::RGBA as AnimatedRGBA;
 use values::animated::effects::Filter as AnimatedFilter;
 use values::animated::effects::FilterList as AnimatedFilterList;
 use values::computed::{Angle, CalcLengthOrPercentage};
 use values::computed::{ClipRect, Context};
@@ -167,17 +167,24 @@ impl From<nsCSSPropertyID> for Transitio
             }
             _ => {
                 panic!("non-convertible nsCSSPropertyID")
             }
         }
     }
 }
 
-impl SpecifiedValueInfo for TransitionProperty {}
+impl SpecifiedValueInfo for TransitionProperty {
+    fn collect_completion_keywords(f: KeywordsCollectFn) {
+        // `transition-property` can actually accept all properties and
+        // arbitrary identifiers, but `all` is a special one we'd like
+        // to list.
+        f(&["all"]);
+    }
+}
 
 /// Returns true if this nsCSSPropertyID is one of the transitionable properties.
 #[cfg(feature = "gecko")]
 pub fn nscsspropertyid_is_transitionable(property: nsCSSPropertyID) -> bool {
     match property {
         % for prop in data.longhands + data.shorthands_except_all():
             % if prop.transitionable:
                 ${prop.nscsspropertyid()} => true,
--- a/servo/components/style/values/generics/font.rs
+++ b/servo/components/style/values/generics/font.rs
@@ -6,17 +6,18 @@
 
 use app_units::Au;
 use byteorder::{BigEndian, ReadBytesExt};
 use cssparser::Parser;
 use num_traits::One;
 use parser::{Parse, ParserContext};
 use std::fmt::{self, Write};
 use std::io::Cursor;
-use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss};
+use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
+use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
 /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
 #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
          ToComputedValue)]
 pub struct FeatureTagValue<Integer> {
     /// A four-character tag, packed into a u32 (one byte per character).
     pub tag: FontTag,
@@ -176,18 +177,20 @@ where
         KeywordInfo {
             kw: x,
             factor: 1.,
             offset: Au(0).into(),
         }
     }
 }
 
-impl<Length> SpecifiedValueInfo for KeywordInfo<Length> {
-    const SUPPORTED_TYPES: u8 = <KeywordSize as SpecifiedValueInfo>::SUPPORTED_TYPES;
+impl<L> SpecifiedValueInfo for KeywordInfo<L> {
+    fn collect_completion_keywords(f: KeywordsCollectFn) {
+        <KeywordSize as SpecifiedValueInfo>::collect_completion_keywords(f);
+    }
 }
 
 /// CSS font keywords
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
          Parse, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero,
          ToCss)]
 #[allow(missing_docs)]
 pub enum KeywordSize {
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -3,17 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Generic types that share their serialization implementations
 //! for both specified and computed values.
 
 use counter_style::{parse_counter_style_name, Symbols};
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use style_traits::{ParseError, StyleParseErrorKind};
+use style_traits::{KeywordsCollectFn, ParseError};
+use style_traits::{SpecifiedValueInfo, StyleParseErrorKind};
 use super::CustomIdent;
 
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 #[path = "box.rs"]
 pub mod box_;
 pub mod column;
@@ -74,18 +75,17 @@ impl SymbolsType {
     }
 }
 
 /// <https://drafts.csswg.org/css-counter-styles/#typedef-counter-style>
 ///
 /// Since wherever <counter-style> is used, 'none' is a valid value as
 /// well, we combine them into one type to make code simpler.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
-#[derive(Clone, Debug, Eq, PartialEq, SpecifiedValueInfo, ToComputedValue,
-         ToCss)]
+#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss)]
 pub enum CounterStyleOrNone {
     /// `none`
     None,
     /// `<counter-style-name>`
     Name(CustomIdent),
     /// `symbols()`
     #[css(function)]
     Symbols(SymbolsType, Symbols),
@@ -133,16 +133,32 @@ impl Parse for CounterStyleOrNone {
                 }
                 Ok(CounterStyleOrNone::Symbols(symbols_type, symbols))
             });
         }
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
+impl SpecifiedValueInfo for CounterStyleOrNone {
+    fn collect_completion_keywords(f: KeywordsCollectFn) {
+        // XXX The best approach for implementing this is probably
+        // having a CounterStyleName type wrapping CustomIdent, and
+        // put the predefined list for that type in counter_style mod.
+        // But that's a non-trivial change itself, so we use a simpler
+        // approach here.
+        macro_rules! predefined {
+            ($($name:expr,)+) => {
+                f(&["none", "symbols", $($name,)+]);
+            }
+        }
+        include!("../../counter_style/predefined.rs");
+    }
+}
+
 /// A wrapper of Non-negative values.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
          PartialEq, PartialOrd, SpecifiedValueInfo, ToAnimatedZero,
          ToComputedValue, ToCss)]
 pub struct NonNegative<T>(pub T);
 
 /// A wrapper of greater-than-or-equal-to-one values.
--- 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"]);
+}
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -12,18 +12,18 @@ use cssparser::{Parser, Token};
 use custom_properties::SpecifiedValue;
 use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")]
 use servo_url::ServoUrl;
 use std::cmp::Ordering;
 use std::f32::consts::PI;
 use std::fmt::{self, Write};
-use style_traits::{CssType, CssWriter, ParseError, StyleParseErrorKind};
-use style_traits::{SpecifiedValueInfo, ToCss};
+use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError};
+use style_traits::{StyleParseErrorKind, SpecifiedValueInfo, ToCss};
 use values::{Either, None_};
 #[cfg(feature = "gecko")]
 use values::computed::{Context, Position as ComputedPosition, ToComputedValue};
 use values::generics::image::{self as generic, Circle, CompatMode, Ellipse, ShapeExtent};
 use values::generics::image::PaintWorklet;
 use values::generics::position::Position as GenericPosition;
 use values::specified::{Angle, Color, Length, LengthOrPercentage};
 use values::specified::{Number, NumberOrPercentage, Percentage, RGBAColor};
@@ -52,16 +52,35 @@ pub type Gradient = generic::Gradient<
     LengthOrPercentage,
     GradientPosition,
     RGBAColor,
     Angle,
 >;
 
 impl SpecifiedValueInfo for Gradient {
     const SUPPORTED_TYPES: u8 = CssType::GRADIENT;
+
+    fn collect_completion_keywords(f: KeywordsCollectFn) {
+        // This list here should keep sync with that in Gradient::parse.
+        f(&[
+          "linear-gradient",
+          "-webkit-linear-gradient",
+          "-moz-linear-gradient",
+          "repeating-linear-gradient",
+          "-webkit-repeating-linear-gradient",
+          "-moz-repeating-linear-gradient",
+          "radial-gradient",
+          "-webkit-radial-gradient",
+          "-moz-radial-gradient",
+          "repeating-radial-gradient",
+          "-webkit-repeating-radial-gradient",
+          "-moz-repeating-radial-gradient",
+          "-webkit-gradient",
+        ]);
+    }
 }
 
 /// A specified gradient kind.
 #[cfg(not(feature = "gecko"))]
 pub type GradientKind =
     generic::GradientKind<LineDirection, Length, LengthOrPercentage, Position, Angle>;
 
 /// A specified gradient kind.
--- a/servo/components/style_traits/cursor.rs
+++ b/servo/components/style_traits/cursor.rs
@@ -1,15 +1,15 @@
 /* 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/. */
 
 //! A list of common mouse cursors per CSS3-UI ยง 8.1.1.
 
-use super::{CssWriter, SpecifiedValueInfo, ToCss};
+use super::{CssWriter, KeywordsCollectFn, SpecifiedValueInfo, ToCss};
 
 macro_rules! define_cursor {
     (
         common properties = [
             $( $c_css: expr => $c_variant: ident = $c_value: expr, )+
         ]
         gecko properties = [
             $( $g_css: expr => $g_variant: ident = $g_value: expr, )+
@@ -53,17 +53,24 @@ macro_rules! define_cursor {
                     })+
                     $(#[cfg(feature = "gecko")] CursorKind::$g_variant => {
                         ::std::fmt::Write::write_str(dest, $g_css)
                     })+
                 }
             }
         }
 
-        impl SpecifiedValueInfo for CursorKind {}
+        impl SpecifiedValueInfo for CursorKind {
+            fn collect_completion_keywords(f: KeywordsCollectFn) {
+                f(&[
+                    $($c_css,)+
+                    $($g_css,)+
+                ]);
+            }
+        }
     }
 }
 
 define_cursor! {
     common properties = [
         "none" => None = 0,
         "default" => Default = 1,
         "pointer" => Pointer = 2,