Bug 1434130 part 12 - Manually implement collect_completion_keywords for some types. r=emilio
authorXidorn Quan <me@upsuper.org>
Sun, 29 Apr 2018 09:03:31 +1000
changeset 416184 1baf959951783147c5d1a0321eb6577a0f6c1e09
parent 416183 b9e4fda9a50f2cc7ab722df3e9549a9b4e653970
child 416185 f31672f0c57afb5235859c3ee0123d05f8bfb360
push id33918
push usernerli@mozilla.com
push dateSun, 29 Apr 2018 09:47:13 +0000
treeherdermozilla-central@afbec7f03bd8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1434130
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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,