Bug 1338936 - Part 6: stylo: Add helpers for converting Gecko keywords to Servo, use to support keyword pres attrs; r?emilio draft
authorManish Goregaokar <manishearth@gmail.com>
Sun, 12 Feb 2017 16:02:29 -0800
changeset 485640 d0d38c57588e34d3737526fb20e84cb38c81d488
parent 485639 10e4bd13cce80d4f9a367a88a5d455abe7d23b1e
child 485641 4c152778adfd3f01c82e0a229a93e04805a70946
push id45797
push userbmo:manishearth@gmail.com
push dateThu, 16 Feb 2017 23:47:53 +0000
reviewersemilio
bugs1338936
milestone54.0a1
Bug 1338936 - Part 6: stylo: Add helpers for converting Gecko keywords to Servo, use to support keyword pres attrs; r?emilio MozReview-Commit-ID: 6wg32flypt7
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/inherited_table.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/list.mako.rs
servo/components/style/properties/longhand/pointing.mako.rs
servo/ports/geckolib/glue.rs
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -343,30 +343,65 @@
     <%call expr="single_keyword_computed(name, values, vector, **kwargs)">
         use values::computed::ComputedValueAsSpecified;
         use values::HasViewportPercentage;
         impl ComputedValueAsSpecified for SpecifiedValue {}
         no_viewport_percentage!(SpecifiedValue);
     </%call>
 </%def>
 
-<%def name="single_keyword_computed(name, values, vector=False, extra_specified=None, **kwargs)">
+<%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue')">
+    <%
+        if not values:
+            values = keyword.values_for(product)
+    %>
+    impl ${type} {
+        /// Obtain a specified value from a Gecko keyword value
+        ///
+        /// Intended for use with presentation attributes, not style structs
+        pub fn from_gecko_keyword(kw: u32) -> Self {
+            use gecko_bindings::structs;
+            % if keyword.gecko_enum_prefix:
+                % for value in values:
+                    // We can't match on enum values if we're matching on a u32
+                    const ${to_rust_ident(value).upper()}: u32
+                        = structs::${keyword.gecko_enum_prefix}::${to_camel_case(value)} as u32;
+                % endfor
+                match kw {
+                    % for value in values:
+                        ${to_rust_ident(value).upper()} => ${type}::${to_rust_ident(value)},
+                    % endfor
+                    x => panic!("Found unexpected value in style struct for ${keyword.name} property: {:?}", x),
+                }
+            % else:
+                match kw {
+                    % for value in values:
+                        structs::${keyword.gecko_constant(value)} => ${type}::${to_rust_ident(value)},
+                    % endfor
+                    x => panic!("Found unexpected value in style struct for ${keyword.name} property: {:?}", x),
+                }
+            % endif
+        }
+    }
+</%def>
+
+<%def name="single_keyword_computed(name, values, vector=False, extra_specified=None, needs_conversion=False, **kwargs)">
     <%
         keyword_kwargs = {a: kwargs.pop(a, None) for a in [
             'gecko_constant_prefix', 'gecko_enum_prefix',
             'extra_gecko_values', 'extra_servo_values',
             'custom_consts', 'gecko_inexhaustive',
         ]}
     %>
 
-    <%def name="inner_body()">
+    <%def name="inner_body(keyword, extra_specified=None, needs_conversion=False)">
         % if extra_specified:
             use style_traits::ToCss;
             define_css_keyword_enum! { SpecifiedValue:
-                % for value in data.longhands_by_name[name].keyword.values_for(product) + extra_specified.split():
+                % for value in keyword.values_for(product) + extra_specified.split():
                     "${value}" => ${to_rust_ident(value)},
                 % endfor
             }
         % else:
             pub use self::computed_value::T as SpecifiedValue;
         % endif
         pub mod computed_value {
             use style_traits::ToCss;
@@ -391,25 +426,35 @@
         }
         impl Parse for SpecifiedValue {
             #[inline]
             fn parse(_context: &ParserContext, input: &mut Parser)
                          -> Result<SpecifiedValue, ()> {
                 SpecifiedValue::parse(input)
             }
         }
+
+        % if needs_conversion:
+            <%
+                conversion_values = keyword.values_for(product)
+                if extra_specified:
+                    conversion_values += extra_specified.split()
+            %>
+            ${gecko_keyword_conversion(keyword, values=conversion_values)}
+        % endif
     </%def>
     % if vector:
         <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
-            ${inner_body()}
+            ${inner_body(Keyword(name, values, **keyword_kwargs))}
             ${caller.body()}
         </%call>
     % else:
         <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
-            ${inner_body()}
+            ${inner_body(Keyword(name, values, **keyword_kwargs),
+                         extra_specified=extra_specified, needs_conversion=needs_conversion)}
             ${caller.body()}
         </%call>
     % endif
 </%def>
 
 <%def name="shorthand(name, sub_properties, experimental=False, **kwargs)">
 <%
     shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental,
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -1,14 +1,14 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>
+<% from data import Keyword, Method, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>
 
 <% data.new_style_struct("Border", inherited=False,
                    additional_methods=[Method("border_" + side + "_has_nonzero_width",
                                               "bool") for side in ["top", "right", "bottom", "left"]]) %>
 <%
     def maybe_logical_spec(side, kind):
         if side[1]: # if it is logical
             return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
@@ -27,16 +27,19 @@
     ${helpers.predefined_type("border-%s-style" % side[0], "BorderStyle",
                               "specified::BorderStyle::none",
                               need_clone=True,
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
                               spec=maybe_logical_spec(side, "style"),
                               animatable=False, logical = side[1])}
 % endfor
 
+${helpers.gecko_keyword_conversion(Keyword('border-style',
+                                   "none solid double dotted dashed hidden groove ridge inset outset"),
+                                   type="::values::specified::BorderStyle")}
 % for side in ALL_SIDES:
     <%helpers:longhand name="border-${side[0]}-width" animatable="True" logical="${side[1]}"
                        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width")
                        spec="${maybe_logical_spec(side, 'width')}">
         use app_units::Au;
         use std::fmt;
         use style_traits::ToCss;
         use values::HasViewportPercentage;
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -1,14 +1,14 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Keyword, Method, to_rust_ident %>
+<% from data import Keyword, Method, to_rust_ident, to_camel_case%>
 
 <% data.new_style_struct("Box",
                          inherited=False,
                          gecko_name="Display") %>
 
 // TODO(SimonSapin): don't parse `inline-table`, since we don't support it
 <%helpers:longhand name="display"
                    need_clone="True"
@@ -88,16 +88,19 @@
                                    _cacheable: &mut bool,
                                    _error_reporter: &mut StdBox<ParseErrorReporter + Send>) {
             longhands::_servo_display_for_hypothetical_box::derive_from_display(context);
             longhands::_servo_text_decorations_in_effect::derive_from_display(context);
             longhands::_servo_under_display_none::derive_from_display(context);
         }
     % endif
 
+    ${helpers.gecko_keyword_conversion(Keyword('display', ' '.join(values),
+                                               gecko_enum_prefix='StyleDisplay'))}
+
 </%helpers:longhand>
 
 ${helpers.single_keyword("-moz-top-layer", "none top",
                          gecko_constant_prefix="NS_STYLE_TOP_LAYER",
                          gecko_ffi_name="mTopLayer", need_clone=True,
                          products="gecko", animatable=False, internal=True,
                          spec="Internal (not web-exposed)")}
 
@@ -141,16 +144,17 @@
         }
     }
 </%helpers:single_keyword_computed>
 
 <%helpers:single_keyword_computed name="float"
                                   values="none left right"
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
+                                  needs_conversion="True"
                                   animatable="False"
                                   need_clone="True"
                                   gecko_enum_prefix="StyleFloat"
                                   gecko_inexhaustive="True"
                                   gecko_ffi_name="mFloat"
                                   spec="https://drafts.csswg.org/css-box/#propdef-float">
     use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
@@ -185,16 +189,17 @@
         }
     }
 </%helpers:single_keyword_computed>
 
 <%helpers:single_keyword_computed name="clear"
                                   values="none left right both"
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
+                                  needs_conversion="True"
                                   animatable="False"
                                   gecko_enum_prefix="StyleClear"
                                   gecko_ffi_name="mBreakType"
                                   spec="https://www.w3.org/TR/CSS2/visuren.html#flow-control">
     use values::HasViewportPercentage;
     no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
@@ -251,16 +256,18 @@
     use values::HasViewportPercentage;
 
     <% vertical_align = data.longhands_by_name["vertical-align"] %>
     <% vertical_align.keyword = Keyword("vertical-align",
                                         "baseline sub super top text-top middle bottom text-bottom",
                                         extra_gecko_values="middle-with-baseline") %>
     <% vertical_align_keywords = vertical_align.keyword.values_for(product) %>
 
+    ${helpers.gecko_keyword_conversion(vertical_align.keyword)}
+
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
             match *self {
                 SpecifiedValue::LengthOrPercentage(ref length) => length.has_viewport_percentage(),
                 _ => false
             }
         }
     }
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -11,16 +11,17 @@
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse")}
 ${helpers.single_keyword("empty-cells", "show hide",
                          gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS",
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells")}
 ${helpers.single_keyword("caption-side", "top bottom",
                          extra_gecko_values="right left top-outside bottom-outside",
+                         needs_conversion="True",
                          animatable=False,
                          spec="https://drafts.csswg.org/css-tables/#propdef-caption-side")}
 
 <%helpers:longhand name="border-spacing" animatable="False" boxed="True"
                    spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing">
     use app_units::Au;
     use std::fmt;
     use style_traits::ToCss;
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -1,14 +1,14 @@
 /* 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/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
-
+<% from data import Keyword %>
 <% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
 
 <%helpers:longhand name="line-height" animatable="True"
                    spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height">
     use std::fmt;
     use style_traits::ToCss;
     use values::{CSSFloat, HasViewportPercentage};
 
@@ -255,16 +255,19 @@
         }
     }
     #[inline] pub fn get_initial_value() -> computed_value::T {
         computed_value::T::start
     }
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         computed_value::T::parse(input)
     }
+    // Gecko only parses a subset of the allowed values in pres attrs.
+    ${helpers.gecko_keyword_conversion(Keyword('text-align',
+                                               "left right center justify"))}
 </%helpers:longhand>
 
 // FIXME: This prop should be animatable.
 <%helpers:longhand name="letter-spacing" animatable="False"
                    spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing">
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
@@ -510,16 +513,17 @@
         let derived = derive(context);
         context.mutate_style().mutate_inheritedtext().set__servo_text_decorations_in_effect(derived);
     }
 </%helpers:longhand>
 
 <%helpers:single_keyword_computed name="white-space"
                                   values="normal pre nowrap pre-wrap pre-line"
                                   gecko_constant_prefix="NS_STYLE_WHITESPACE"
+                                  needs_conversion="True"
                                   animatable="False"
                                   spec="https://drafts.csswg.org/css-text/#propdef-white-space">
     use values::computed::ComputedValueAsSpecified;
     use values::HasViewportPercentage;
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     impl SpecifiedValue {
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -20,16 +20,17 @@
 ${helpers.single_keyword("list-style-type", """
     disc none circle square decimal disclosure-open disclosure-closed
 """, extra_servo_values="""arabic-indic bengali cambodian cjk-decimal devanagari
                            gujarati gurmukhi kannada khmer lao malayalam mongolian
                            myanmar oriya persian telugu thai tibetan cjk-earthly-branch
                            cjk-heavenly-stem lower-greek hiragana hiragana-iroha katakana
                            katakana-iroha lower-alpha upper-alpha""",
     gecko_constant_prefix="NS_STYLE_LIST_STYLE",
+    needs_conversion="True",
     animatable=False,
     spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type")}
 
 ${helpers.predefined_type("list-style-image", "UrlOrNone", "Either::Second(None_)",
                           animatable=False,
                           spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image")}
 
 <%helpers:longhand name="quotes" animatable="False"
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -154,17 +154,17 @@
                          gecko_enum_prefix="StyleUserInput",
                          gecko_inexhaustive=True,
                          animatable=False,
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)")}
 
 ${helpers.single_keyword("-moz-user-modify", "read-only read-write write-only",
                          products="gecko", gecko_ffi_name="mUserModify",
                          gecko_enum_prefix="StyleUserModify",
-                         gecko_inexhaustive=True,
+                         needs_conversion=True,
                          animatable=False,
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-modify)")}
 
 ${helpers.single_keyword("-moz-user-focus",
                          "none ignore normal select-after select-before select-menu select-same select-all",
                          products="gecko", gecko_ffi_name="mUserFocus",
                          gecko_enum_prefix="StyleUserFocus",
                          gecko_inexhaustive=True,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1019,21 +1019,50 @@ pub extern "C" fn Servo_DeclarationBlock
                                                              nsCSSPropertyID,
                                                              _:
                                                              *const nsAString) {
     // 
     error!("stylo: Don't know how to handle ident presentation attributes (-x-lang)");
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(_:
+#[allow(unreachable_code)]
+pub extern "C" fn Servo_DeclarationBlock_SetKeywordValue(declarations:
                                                          RawServoDeclarationBlockBorrowed,
-                                                         _: nsCSSPropertyID,
-                                                         _: i32) {
+                                                         property: nsCSSPropertyID,
+                                                         value: i32) {
+    use style::properties::{DeclaredValue, PropertyDeclaration, LonghandId};
+    use style::properties::longhands;
+    use style::values::specified::{BorderStyle, NoCalcLength};
+
+    let declarations = RwLock::<PropertyDeclarationBlock>::as_arc(&declarations);
+    let long = get_longhand_from_id!(property);
+    let value = value as u32;
 
+    let prop = match_wrap_declared! { long,
+        MozUserModify => longhands::_moz_user_modify::SpecifiedValue::from_gecko_keyword(value),
+        // TextEmphasisPosition => FIXME implement text-emphasis-position
+        Display => longhands::display::SpecifiedValue::from_gecko_keyword(value),
+        Float => longhands::float::SpecifiedValue::from_gecko_keyword(value),
+        VerticalAlign => longhands::vertical_align::SpecifiedValue::from_gecko_keyword(value),
+        TextAlign => longhands::text_align::SpecifiedValue::from_gecko_keyword(value),
+        Clear => longhands::clear::SpecifiedValue::from_gecko_keyword(value),
+        FontSize => {
+            // We rely on Gecko passing in font-size values (0...7) here.
+            longhands::font_size::SpecifiedValue(NoCalcLength::from_font_size_int(value as u8).into())
+        },
+        ListStyleType => longhands::list_style_type::SpecifiedValue::from_gecko_keyword(value),
+        WhiteSpace => longhands::white_space::SpecifiedValue::from_gecko_keyword(value),
+        CaptionSide => longhands::caption_side::SpecifiedValue::from_gecko_keyword(value),
+        BorderTopStyle => BorderStyle::from_gecko_keyword(value),
+        BorderRightStyle => BorderStyle::from_gecko_keyword(value),
+        BorderBottomStyle => BorderStyle::from_gecko_keyword(value),
+        BorderLeftStyle => BorderStyle::from_gecko_keyword(value),
+    };
+    declarations.write().declarations.push((prop, Default::default()));
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SetIntValue(_: RawServoDeclarationBlockBorrowed,
                                                      _: nsCSSPropertyID,
                                                      _: i32) {
     error!("stylo: Don't know how to handle integer presentation attributes (-x-span)");
 }
@@ -1186,17 +1215,17 @@ pub extern "C" fn Servo_DeclarationBlock
             declarations.write().declarations.push((decl, Default::default()));
         }
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SetTextDecorationColorOverride(_:
                                                         RawServoDeclarationBlockBorrowed) {
-
+    error!("stylo: Don't know how to handle quirks-mode text-decoration color overrides");
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_CSSSupports2(property: *const nsACString, value: *const nsACString) -> bool {
     let property = unsafe { property.as_ref().unwrap().as_str_unchecked() };
     let id =  if let Ok(id) = PropertyId::parse(property.into()) {
         id
     } else {