servo: Merge #17821 - Implement restrictions on which properties apply to which pseudo-elements (from bzbarsky:pseudo-prop-restrictions); r=emilio
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 21 Jul 2017 12:15:50 -0700
changeset 419003 ffeeb5f34457e9ea4d46c500d6218cf49aa4f833
parent 419002 f5a5e055be578be73d2a4dce8018f97f341f276a
child 419004 4d83b498dc00fdfd50509d4a6f087d426a01a08d
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs17821, 1382786
milestone56.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
servo: Merge #17821 - Implement restrictions on which properties apply to which pseudo-elements (from bzbarsky:pseudo-prop-restrictions); r=emilio ::first-line, ::first-letter, and ::placeholder don't allow all properties to apply to them. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix https://bugzilla.mozilla.org/show_bug.cgi?id=1382786 <!-- Either: --> - [ ] There are tests for these changes OR - [X] These changes do not require tests because they're Gecko-only for the moment, and Gecko has tests. <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 4bf8e4413ee5f62dfb81f07f0cff8ebded2ef230
servo/components/style/gecko/pseudo_element.rs
servo/components/style/properties/longhand/background.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/color.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/margin.mako.rs
servo/components/style/properties/longhand/padding.mako.rs
servo/components/style/properties/longhand/pointing.mako.rs
servo/components/style/properties/longhand/text.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/servo/selector_parser.rs
--- a/servo/components/style/gecko/pseudo_element.rs
+++ b/servo/components/style/gecko/pseudo_element.rs
@@ -5,16 +5,18 @@
 //! Gecko's definition of a pseudo-element.
 //!
 //! Note that a few autogenerated bits of this live in
 //! `pseudo_element_definition.mako.rs`. If you touch that file, you probably
 //! need to update the checked-in files for Servo.
 
 use cssparser::{ToCss, serialize_identifier};
 use gecko_bindings::structs::{self, CSSPseudoElementType};
+use properties::{PropertyFlags, APPLIES_TO_FIRST_LETTER, APPLIES_TO_FIRST_LINE};
+use properties::APPLIES_TO_PLACEHOLDER;
 use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
 use std::fmt;
 use string_cache::Atom;
 
 include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_definition.rs"));
 
 impl ::selectors::parser::PseudoElement for PseudoElement {
     type Impl = SelectorImpl;
@@ -114,9 +116,20 @@ impl PseudoElement {
     /// Covert non-canonical pseudo-element to canonical one, and keep a
     /// canonical one as it is.
     pub fn canonical(&self) -> PseudoElement {
         match *self {
             PseudoElement::MozPlaceholder => PseudoElement::Placeholder,
             _ => self.clone(),
         }
     }
+
+    /// Property flag that properties must have to apply to this pseudo-element.
+    #[inline]
+    pub fn property_restriction(&self) -> Option<PropertyFlags> {
+        match *self {
+            PseudoElement::FirstLetter => Some(APPLIES_TO_FIRST_LETTER),
+            PseudoElement::FirstLine => Some(APPLIES_TO_FIRST_LINE),
+            PseudoElement::Placeholder => Some(APPLIES_TO_PLACEHOLDER),
+            _ => None,
+        }
+    }
 }
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -7,41 +7,45 @@
 <% data.new_style_struct("Background", inherited=False) %>
 
 ${helpers.predefined_type("background-color", "Color",
     "computed_value::T::transparent()",
     initial_specified_value="SpecifiedValue::transparent()",
     spec="https://drafts.csswg.org/css-backgrounds/#background-color",
     animation_value_type="IntermediateColor",
     ignored_when_colors_disabled=True,
-    allow_quirks=True)}
+    allow_quirks=True,
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
 
 ${helpers.predefined_type("background-image", "ImageLayer",
     initial_value="Either::First(None_)",
     initial_specified_value="Either::First(None_)",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
     vector="True",
     animation_value_type="discrete",
     has_uncacheable_values="True" if product == "gecko" else "False",
-    ignored_when_colors_disabled="True")}
+    ignored_when_colors_disabled="True",
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
 
 % for (axis, direction, initial) in [("x", "Horizontal", "left"), ("y", "Vertical", "top")]:
     ${helpers.predefined_type(
         "background-position-" + axis,
         "position::" + direction + "Position",
         initial_value="computed::LengthOrPercentage::zero()",
         initial_specified_value="SpecifiedValue::initial_specified_value()",
         spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
         animation_value_type="ComputedValue",
         vector=True,
+        flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
     )}
 % endfor
 
 <%helpers:vector_longhand name="background-repeat" animation_value_type="discrete"
-                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat">
+                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat"
+                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER">
     use std::fmt;
     use style_traits::ToCss;
 
     define_css_keyword_enum!(RepeatKeyword:
                              "repeat" => Repeat,
                              "space" => Space,
                              "round" => Round,
                              "no-repeat" => NoRepeat);
@@ -133,41 +137,46 @@
     }
 </%helpers:vector_longhand>
 
 ${helpers.single_keyword("background-attachment",
                          "scroll fixed" + (" local" if product == "gecko" else ""),
                          vector=True,
                          gecko_constant_prefix="NS_STYLE_IMAGELAYER_ATTACHMENT",
                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment",
-                         animation_value_type="discrete")}
+                         animation_value_type="discrete",
+                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
 
 ${helpers.single_keyword("background-clip",
                          "border-box padding-box content-box",
                          extra_gecko_values="text",
                          vector=True, extra_prefixes="webkit",
                          gecko_enum_prefix="StyleGeometryBox",
                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-clip",
-                         animation_value_type="discrete")}
+                         animation_value_type="discrete",
+                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
 
 ${helpers.single_keyword("background-origin",
                          "padding-box border-box content-box",
                          vector=True, extra_prefixes="webkit",
                          gecko_enum_prefix="StyleGeometryBox",
                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-origin",
-                         animation_value_type="discrete")}
+                         animation_value_type="discrete",
+                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
 
 ${helpers.predefined_type("background-size", "BackgroundSize",
     initial_value="computed::LengthOrPercentageOrAuto::Auto.into()",
     initial_specified_value="specified::LengthOrPercentageOrAuto::Auto.into()",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
     vector=True,
     animation_value_type="ComputedValue",
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
     extra_prefixes="webkit")}
 
 // https://drafts.fxtf.org/compositing/#background-blend-mode
 ${helpers.single_keyword("background-blend-mode",
                          """normal multiply screen overlay darken lighten color-dodge
                             color-burn hard-light soft-light difference exclusion hue
                             saturation color luminosity""",
                          gecko_constant_prefix="NS_STYLE_BLEND",
                          vector=True, products="gecko", animation_value_type="discrete",
-                         spec="https://drafts.fxtf.org/compositing/#background-blend-mode")}
+                         spec="https://drafts.fxtf.org/compositing/#background-blend-mode",
+                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -22,56 +22,61 @@
     %>
     ${helpers.predefined_type("border-%s-color" % side_name, "Color",
                               "computed_value::T::currentcolor()",
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
                               spec=maybe_logical_spec(side, "color"),
                               animation_value_type="IntermediateColor",
                               logical=is_logical,
                               allow_quirks=not is_logical,
+                              flags="APPLIES_TO_FIRST_LETTER",
                               ignored_when_colors_disabled=True)}
 
     ${helpers.predefined_type("border-%s-style" % side_name, "BorderStyle",
                               "specified::BorderStyle::none",
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
                               spec=maybe_logical_spec(side, "style"),
+                              flags="APPLIES_TO_FIRST_LETTER",
                               animation_value_type="discrete" if not is_logical else "none",
                               logical=is_logical)}
 
     ${helpers.predefined_type("border-%s-width" % side_name,
                               "BorderSideWidth",
                               "Au::from_px(3)",
                               computed_type="::app_units::Au",
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
                               spec=maybe_logical_spec(side, "width"),
                               animation_value_type="ComputedValue",
                               logical=is_logical,
+                              flags="APPLIES_TO_FIRST_LETTER",
                               allow_quirks=not is_logical)}
 % endfor
 
 ${helpers.gecko_keyword_conversion(Keyword('border-style',
                                    "none solid double dotted dashed hidden groove ridge inset outset"),
                                    type="::values::specified::BorderStyle")}
 
 // FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
 % for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
     ${helpers.predefined_type("border-" + corner + "-radius", "BorderCornerRadius",
                               "computed::LengthOrPercentage::zero().into()",
                               "parse", extra_prefixes="webkit",
                               spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner,
                               boxed=True,
+                              flags="APPLIES_TO_FIRST_LETTER",
                               animation_value_type="ComputedValue")}
 % endfor
 
 /// -moz-border-*-colors: color, string, enum, none, inherit/initial
 /// These non-spec properties are just for Gecko (Stylo) internal use.
 % for side in PHYSICAL_SIDES:
     <%helpers:longhand name="-moz-border-${side}-colors" animation_value_type="discrete"
                        spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-border-*-colors)"
                        products="gecko"
+                       flags="APPLIES_TO_FIRST_LETTER"
                        ignored_when_colors_disabled="True">
         use std::fmt;
         use style_traits::ToCss;
         use values::specified::RGBAColor;
         no_viewport_percentage!(SpecifiedValue);
 
         pub mod computed_value {
             use cssparser::RGBA;
@@ -201,27 +206,30 @@
 
 ${helpers.predefined_type("border-image-source", "ImageLayer",
     initial_value="Either::First(None_)",
     initial_specified_value="Either::First(None_)",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
     vector=False,
     animation_value_type="discrete",
     has_uncacheable_values=False,
+    flags="APPLIES_TO_FIRST_LETTER",
     boxed="True")}
 
 ${helpers.predefined_type("border-image-outset", "LengthOrNumberRect",
     parse_method="parse_non_negative",
     initial_value="computed::LengthOrNumber::zero().into()",
     initial_specified_value="specified::LengthOrNumber::zero().into()",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
     animation_value_type="discrete",
+    flags="APPLIES_TO_FIRST_LETTER",
     boxed=True)}
 
 <%helpers:longhand name="border-image-repeat" animation_value_type="discrete"
+                   flags="APPLIES_TO_FIRST_LETTER"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::RepeatKeyword;
 
@@ -273,23 +281,25 @@
     }
 </%helpers:longhand>
 
 ${helpers.predefined_type("border-image-width", "BorderImageWidth",
     initial_value="computed::BorderImageSideWidth::one().into()",
     initial_specified_value="specified::BorderImageSideWidth::one().into()",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
     animation_value_type="discrete",
+    flags="APPLIES_TO_FIRST_LETTER",
     boxed=True)}
 
 ${helpers.predefined_type("border-image-slice", "BorderImageSlice",
     initial_value="computed::NumberOrPercentage::Percentage(computed::Percentage(1.)).into()",
     initial_specified_value="specified::NumberOrPercentage::Percentage(specified::Percentage::new(1.)).into()",
     spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
     animation_value_type="discrete",
+    flags="APPLIES_TO_FIRST_LETTER",
     boxed=True)}
 
 #[cfg(feature = "gecko")]
 impl ::values::computed::BorderImageWidth {
     pub fn to_gecko_rect(&self, sides: &mut ::gecko_bindings::structs::nsStyleSides) {
         use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
         use gecko::values::GeckoStyleCoordConvertible;
         use values::generics::border::BorderImageSideWidth;
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -5,20 +5,24 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% 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
+//
+// We allow "display" to apply to placeholders because we need to make the
+// placeholder pseudo-element an inline-block in the UA stylesheet in Gecko.
 <%helpers:longhand name="display"
                    need_clone="True"
                    animation_value_type="discrete"
                    custom_cascade="${product == 'servo'}"
+                   flags="APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-display/#propdef-display">
     <%
         values = """inline block inline-block
             table inline-table table-row-group table-header-group table-footer-group
             table-row table-column-group table-column table-cell table-caption
             list-item none
         """.split()
         webkit_prefixed_values = "flex inline-flex".split()
@@ -211,16 +215,17 @@
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
                                   needs_conversion="True"
                                   animation_value_type="discrete"
                                   gecko_enum_prefix="StyleFloat"
                                   gecko_inexhaustive="True"
                                   gecko_ffi_name="mFloat"
                                   gecko_pref_ident="float_"
+                                  flags="APPLIES_TO_FIRST_LETTER"
                                   spec="https://drafts.csswg.org/css-box/#propdef-float">
     no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let ltr = context.style().writing_mode.is_bidi_ltr();
@@ -301,16 +306,17 @@
     pub fn derive_from_display(context: &mut Context) {
         let d = context.style().get_box().clone_display();
         context.builder.set__servo_display_for_hypothetical_box(d);
     }
 
 </%helpers:longhand>
 
 <%helpers:longhand name="vertical-align" animation_value_type="ComputedValue"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                    spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align">
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::AllowQuirks;
 
     <% vertical_align = data.longhands_by_name["vertical-align"] %>
     <% vertical_align.keyword = Keyword("vertical-align",
                                         "baseline sub super top text-top middle bottom text-bottom",
@@ -427,33 +433,36 @@
 
 ${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box",
     products="servo", animation_value_type="none", internal=True,
     spec="Internal, not web-exposed, \
           may be standardized in the future (https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)")}
 
 ${helpers.single_keyword("overflow-clip-box", "padding-box content-box",
     products="gecko", animation_value_type="discrete", internal=True,
+    flags="APPLIES_TO_PLACEHOLDER",
     spec="Internal, not web-exposed, \
           may be standardized in the future (https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)")}
 
 <%
     overflow_custom_consts = { "-moz-hidden-unscrollable": "CLIP" }
 %>
 
 // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
 ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
                          need_clone=True, animation_value_type="discrete",
                          extra_gecko_values="-moz-hidden-unscrollable",
                          custom_consts=overflow_custom_consts,
                          gecko_constant_prefix="NS_STYLE_OVERFLOW",
+                         flags="APPLIES_TO_PLACEHOLDER",
                          spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-x")}
 
 // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`.
 <%helpers:longhand name="overflow-y" need_clone="True" animation_value_type="discrete"
+                   flags="APPLIES_TO_PLACEHOLDER",
                    spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-y">
     pub use super::overflow_x::{SpecifiedValue, parse, get_initial_value, computed_value};
 </%helpers:longhand>
 
 ${helpers.predefined_type("transition-duration",
                           "Time",
                           "computed::Time::zero()",
                           initial_specified_value="specified::Time::zero()",
@@ -1613,20 +1622,25 @@
                          gecko_ffi_name="mBreakInside",
                          gecko_constant_prefix="NS_STYLE_PAGE_BREAK",
                          spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-inside",
                          animation_value_type="discrete")}
 
 // CSS Basic User Interface Module Level 3
 // http://dev.w3.org/csswg/css-ui
 // FIXME support logical values `block` and `inline` (https://drafts.csswg.org/css-logical-props/#resize)
+//
+// This is APPLIES_TO_PLACEHOLDER so we can override, in the UA sheet, the
+// 'resize' property we'd inherit from textarea otherwise.  Basically, just
+// makes the UA rules easier to write.
 ${helpers.single_keyword("resize",
                          "none both horizontal vertical",
                          products="gecko",
                          spec="https://drafts.csswg.org/css-ui/#propdef-resize",
+                         flags="APPLIES_TO_PLACEHOLDER",
                          animation_value_type="discrete")}
 
 
 ${helpers.predefined_type("perspective",
                           "LengthOrNone",
                           "Either::Second(None_)",
                           "parse_non_negative_length",
                           gecko_ffi_name="mChildPerspective",
@@ -1883,16 +1897,17 @@
         }
     }
 </%helpers:longhand>
 
 ${helpers.predefined_type("shape-outside", "basic_shape::FloatAreaShape",
                           "generics::basic_shape::ShapeSource::None",
                           products="gecko", boxed="True",
                           animation_value_type="none",
+                          flags="APPLIES_TO_FIRST_LETTER",
                           spec="https://drafts.csswg.org/css-shapes/#shape-outside-property")}
 
 <%helpers:longhand name="touch-action"
                    products="gecko"
                    animation_value_type="discrete"
                    disable_when_testing="True"
                    spec="https://compat.spec.whatwg.org/#touch-action">
     use gecko_bindings::structs;
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -6,16 +6,17 @@
 
 <% data.new_style_struct("Color", inherited=True) %>
 
 <% from data import to_rust_ident %>
 
 <%helpers:longhand name="color" need_clone="True"
                    animation_value_type="IntermediateRGBA"
                    ignored_when_colors_disabled="True"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-color/#color">
     use cssparser::RGBA;
     use values::specified::{AllowQuirks, Color};
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -6,27 +6,28 @@
 
 // Box-shadow, etc.
 <% data.new_style_struct("Effects", inherited=False) %>
 
 ${helpers.predefined_type("opacity",
                           "Opacity",
                           "1.0",
                           animation_value_type="ComputedValue",
-                          flags="CREATES_STACKING_CONTEXT",
+                          flags="CREATES_STACKING_CONTEXT APPLIES_TO_PLACEHOLDER",
                           spec="https://drafts.csswg.org/css-color/#opacity")}
 
 ${helpers.predefined_type(
     "box-shadow",
     "BoxShadow",
     None,
     vector=True,
     animation_value_type="AnimatedBoxShadowList",
     extra_prefixes="webkit",
     ignored_when_colors_disabled=True,
+    flags="APPLIES_TO_FIRST_LETTER",
     spec="https://drafts.csswg.org/css-backgrounds/#box-shadow",
 )}
 
 ${helpers.predefined_type("clip",
                           "ClipRectOrAuto",
                           "computed::ClipRectOrAuto::auto()",
                           animation_value_type="ComputedValue",
                           boxed="True",
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -64,16 +64,17 @@ macro_rules! impl_gecko_keyword_conversi
 
         fn from_computed_value(other: &computed_value::T) -> Self {
             SpecifiedValue::Value(other.clone())
         }
     }
 </%def>
 
 <%helpers:longhand name="font-family" animation_value_type="discrete" need_index="True"  boxed="${product == 'gecko'}"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-family">
     use properties::longhands::system_font::SystemFont;
     use self::computed_value::{FontFamily, FamilyName};
     use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
@@ -374,35 +375,38 @@ macro_rules! impl_gecko_keyword_conversi
     }
 </%helpers:longhand>
 
 ${helpers.single_keyword_system("font-style",
                                 "normal italic oblique",
                                 gecko_constant_prefix="NS_FONT_STYLE",
                                 gecko_ffi_name="mFont.style",
                                 spec="https://drafts.csswg.org/css-fonts/#propdef-font-style",
+                                flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                                 animation_value_type="discrete")}
 
 
 <% font_variant_caps_custom_consts= { "small-caps": "SMALLCAPS",
                                       "all-small-caps": "ALLSMALL",
                                       "petite-caps": "PETITECAPS",
                                       "all-petite-caps": "ALLPETITE",
                                       "titling-caps": "TITLING" } %>
 
 ${helpers.single_keyword_system("font-variant-caps",
                                "normal small-caps",
                                extra_gecko_values="all-small-caps petite-caps all-petite-caps unicase titling-caps",
                                gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
                                gecko_ffi_name="mFont.variantCaps",
                                spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
                                custom_consts=font_variant_caps_custom_consts,
+                               flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                                animation_value_type="discrete")}
 
 <%helpers:longhand name="font-weight" need_clone="True" animation_value_type="ComputedValue"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight">
     use properties::longhands::system_font::SystemFont;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     #[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)]
     pub enum SpecifiedValue {
@@ -554,16 +558,17 @@ macro_rules! impl_gecko_keyword_conversi
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue::Weight(*computed)
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="font-size" need_clone="True" animation_value_type="ComputedValue"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    allow_quirks="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size">
     use app_units::Au;
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::{HasViewportPercentage, ToCss};
     use values::FONT_MEDIUM_PX;
     use values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage, NoCalcLength};
     use values::specified::length::FontBaseSize;
@@ -1015,16 +1020,17 @@ macro_rules! impl_gecko_keyword_conversi
             let device = context.builder.device;
             context.builder.mutate_font().fixup_font_min_size(device);
         % endif
         context.builder.font_size_keyword = Some((Default::default(), 1.));
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-size-adjust" animation_value_type="ComputedValue"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
     use properties::longhands::system_font::SystemFont;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Clone, Copy, Debug, PartialEq, ToCss)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
@@ -1136,16 +1142,17 @@ macro_rules! impl_gecko_keyword_conversi
             return Ok(SpecifiedValue::None);
         }
 
         Ok(SpecifiedValue::Number(Number::parse_non_negative(context, input)?))
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-synthesis" animation_value_type="discrete"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis">
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
@@ -1233,27 +1240,30 @@ macro_rules! impl_gecko_keyword_conversi
 ${helpers.single_keyword_system("font-stretch",
                                 "normal ultra-condensed extra-condensed condensed \
                                  semi-condensed semi-expanded expanded extra-expanded \
                                  ultra-expanded",
                                 gecko_ffi_name="mFont.stretch",
                                 gecko_constant_prefix="NS_FONT_STRETCH",
                                 cast_type='i16',
                                 spec="https://drafts.csswg.org/css-fonts/#propdef-font-stretch",
+                                flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                                 animation_value_type="ComputedValue")}
 
 ${helpers.single_keyword_system("font-kerning",
                                 "auto none normal",
                                 products="gecko",
                                 gecko_ffi_name="mFont.kerning",
                                 gecko_constant_prefix="NS_FONT_KERNING",
                                 spec="https://drafts.csswg.org/css-fonts/#propdef-font-kerning",
+                                flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                                 animation_value_type="discrete")}
 
 <%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="none"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use values::CustomIdent;
 
     no_viewport_percentage!(SpecifiedValue);
 
@@ -1453,16 +1463,17 @@ macro_rules! exclusive_value {
             return Err(())
         } else {
             $ident
         }
     }
 }
 
 <%helpers:longhand name="font-variant-east-asian" products="gecko" animation_value_type="discrete"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
@@ -1598,16 +1609,17 @@ macro_rules! exclusive_value {
         }
     }
 
     #[cfg(feature = "gecko")]
     impl_gecko_keyword_conversions!(VariantEastAsian, u16);
 </%helpers:longhand>
 
 <%helpers:longhand name="font-variant-ligatures" products="gecko" animation_value_type="discrete"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
@@ -1757,16 +1769,17 @@ macro_rules! exclusive_value {
         }
     }
 
     #[cfg(feature = "gecko")]
     impl_gecko_keyword_conversions!(VariantLigatures, u16);
 </%helpers:longhand>
 
 <%helpers:longhand name="font-variant-numeric" products="gecko" animation_value_type="discrete"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
@@ -1910,20 +1923,22 @@ macro_rules! exclusive_value {
 </%helpers:longhand>
 
 ${helpers.single_keyword_system("font-variant-position",
                                 "normal sub super",
                                 products="gecko",
                                 gecko_ffi_name="mFont.variantPosition",
                                 gecko_constant_prefix="NS_FONT_VARIANT_POSITION",
                                 spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-position",
+                                flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                                 animation_value_type="discrete")}
 
 <%helpers:longhand name="font-feature-settings" products="gecko" animation_value_type="discrete"
                    extra_prefixes="moz" boxed="True"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings">
     use properties::longhands::system_font::SystemFont;
     use values::generics::FontSettings;
 
     #[derive(Debug, Clone, PartialEq, ToCss)]
     pub enum SpecifiedValue {
         Value(computed_value::T),
         System(SystemFont)
@@ -1956,16 +1971,17 @@ macro_rules! exclusive_value {
 
 <%
 # This spec link is too long to fit elsewhere
 variation_spec = """\
 https://drafts.csswg.org/css-fonts-4/#low-level-font-variation-settings-control-the-font-variation-settings-property\
 """
 %>
 <%helpers:longhand name="font-variation-settings" products="gecko" animation_value_type="none"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="${variation_spec}">
     use values::computed::ComputedValueAsSpecified;
     use values::generics::FontSettings;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
 
     pub type SpecifiedValue = computed_value::T;
 
@@ -1986,16 +2002,17 @@ https://drafts.csswg.org/css-fonts-4/#lo
     pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         computed_value::T::parse(context, input)
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="font-language-override" products="gecko" animation_value_type="discrete"
                    extra_prefixes="moz" boxed="True"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use byteorder::{BigEndian, ByteOrder};
     no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Debug, Clone, PartialEq, Eq)]
@@ -2545,16 +2562,17 @@ https://drafts.csswg.org/css-fonts-4/#lo
 % endif
 
 ${helpers.single_keyword("-moz-osx-font-smoothing",
                          "auto grayscale",
                          gecko_constant_prefix="NS_FONT_SMOOTHING",
                          gecko_ffi_name="mFont.smoothing",
                          products="gecko",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth)",
+                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                          animation_value_type="discrete")}
 
 ${helpers.predefined_type("-moz-min-font-size-ratio",
                           "Percentage",
                           "computed::Percentage::hundred()",
                           animation_value_type="none",
                           products="gecko",
                           internal=True,
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -5,25 +5,27 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 <% from data import Keyword %>
 <% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
 
 ${helpers.predefined_type("line-height",
                           "LineHeight",
                           "computed::LineHeight::normal()",
                           animation_value_type="ComputedValue",
+                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                           spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height")}
 
 // CSS Text Module Level 3
 
 // TODO(pcwalton): `full-width`
 ${helpers.single_keyword("text-transform",
                          "none capitalize uppercase lowercase",
                          extra_gecko_values="full-width",
                          animation_value_type="discrete",
+                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                          spec="https://drafts.csswg.org/css-text/#propdef-text-transform")}
 
 ${helpers.single_keyword("hyphens", "manual none auto",
                          gecko_enum_prefix="StyleHyphens",
                          gecko_inexhaustive=True,
                          products="gecko", animation_value_type="discrete", extra_prefixes="moz",
                          spec="https://drafts.csswg.org/css-text/#propdef-hyphens")}
 
@@ -61,16 +63,17 @@
 // TODO(pcwalton): Support `text-justify: distribute`.
 <%helpers:single_keyword_computed name="text-justify"
                                   values="auto none inter-word"
                                   extra_gecko_values="inter-character"
                                   extra_specified="${'distribute' if product == 'gecko' else ''}"
                                   gecko_enum_prefix="StyleTextJustify"
                                   gecko_inexhaustive="True"
                                   animation_value_type="discrete"
+                                  flags="APPLIES_TO_PLACEHOLDER",
                                   spec="https://drafts.csswg.org/css-text/#propdef-text-justify">
     no_viewport_percentage!(SpecifiedValue);
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, _: &Context) -> computed_value::T {
@@ -104,16 +107,17 @@
                          "auto start end left right center justify",
                          products="gecko",
                          gecko_constant_prefix="NS_STYLE_TEXT_ALIGN",
                          animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-text/#propdef-text-align-last")}
 
 // TODO make this a shorthand and implement text-align-last/text-align-all
 <%helpers:longhand name="text-align" animation_value_type="discrete"
+                   flags="APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-text/#propdef-text-align">
     no_viewport_percentage!(SpecifiedValue);
     pub mod computed_value {
         use style_traits::ToCss;
         macro_rules! define_text_align {
             ( $( $name: ident ( $string: expr ) => $discriminant: expr, )+ ) => {
                 define_css_keyword_enum! { T:
                     $(
@@ -262,22 +266,24 @@
         }
     % endif
 </%helpers:longhand>
 
 ${helpers.predefined_type("letter-spacing",
                           "LetterSpacing",
                           "computed::LetterSpacing::normal()",
                           animation_value_type="ComputedValue",
+                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                           spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing")}
 
 ${helpers.predefined_type("word-spacing",
                           "WordSpacing",
                           "computed::WordSpacing::normal()",
                           animation_value_type="ComputedValue",
+                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                           spec="https://drafts.csswg.org/css-text/#propdef-word-spacing")}
 
 <%helpers:longhand name="-servo-text-decorations-in-effect"
                    derived_from="display text-decoration"
                    need_clone="True" products="servo"
                    animation_value_type="none"
                    spec="Nonstandard (Internal property used by Servo)">
     use cssparser::RGBA;
@@ -362,16 +368,19 @@
 
 <%helpers:single_keyword_computed name="white-space"
                                   values="normal pre nowrap pre-wrap pre-line"
                                   extra_gecko_values="-moz-pre-space"
                                   gecko_enum_prefix="StyleWhiteSpace"
                                   needs_conversion="True"
                                   gecko_inexhaustive="True"
                                   animation_value_type="discrete"
+                                  // Only allowed for UA sheets, which set it
+                                  // !important.
+                                  flags="APPLIES_TO_PLACEHOLDER"
                                   spec="https://drafts.csswg.org/css-text/#propdef-white-space">
     use values::computed::ComputedValueAsSpecified;
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     % if product != "gecko":
     impl SpecifiedValue {
         pub fn allow_wrap(&self) -> bool {
@@ -409,16 +418,17 @@
 
 ${helpers.predefined_type(
     "text-shadow",
     "SimpleShadow",
     None,
     vector=True,
     animation_value_type="AnimatedTextShadowList",
     ignored_when_colors_disabled=True,
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
     spec="https://drafts.csswg.org/css-text-decor-3/#text-shadow-property",
 )}
 
 <%helpers:longhand name="text-emphasis-style" products="gecko" boxed="True"
                    animation_value_type="discrete"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style">
     use computed_values::writing_mode::T as writing_mode;
     use std::fmt;
@@ -713,32 +723,35 @@
 
 // CSS Compatibility
 // https://compat.spec.whatwg.org
 ${helpers.predefined_type(
     "-webkit-text-fill-color", "Color",
     "computed_value::T::currentcolor()",
     products="gecko", animation_value_type="IntermediateColor",
     need_clone=True, ignored_when_colors_disabled=True,
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
     spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color")}
 
 ${helpers.predefined_type(
     "-webkit-text-stroke-color", "Color",
     "computed_value::T::currentcolor()",
     initial_specified_value="specified::Color::currentcolor()",
     products="gecko", animation_value_type="IntermediateColor",
     need_clone=True, ignored_when_colors_disabled=True,
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
     spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color")}
 
 ${helpers.predefined_type("-webkit-text-stroke-width",
                           "BorderSideWidth",
                           "Au::from_px(0)",
                           initial_specified_value="specified::BorderSideWidth::Length(specified::Length::zero())",
                           computed_type="::app_units::Au",
                           products="gecko",
+                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                           spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width",
                           animation_value_type="none")}
 
 // CSS Ruby Layout Module Level 1
 // https://drafts.csswg.org/css-ruby/
 ${helpers.single_keyword("ruby-align", "space-around start center space-between",
                          products="gecko", animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-ruby/#ruby-align-property")}
--- a/servo/components/style/properties/longhand/margin.mako.rs
+++ b/servo/components/style/properties/longhand/margin.mako.rs
@@ -12,10 +12,11 @@
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-margin-%s" % side[1]
     %>
     ${helpers.predefined_type("margin-%s" % side[0], "LengthOrPercentageOrAuto",
                               "computed::LengthOrPercentageOrAuto::Length(Au(0))",
                               alias=maybe_moz_logical_alias(product, side, "-moz-margin-%s"),
                               allow_quirks=not side[1],
                               animation_value_type="ComputedValue", logical = side[1], spec = spec,
+                              flags="APPLIES_TO_FIRST_LETTER",
                               allowed_in_page_rule=True)}
 % endfor
--- a/servo/components/style/properties/longhand/padding.mako.rs
+++ b/servo/components/style/properties/longhand/padding.mako.rs
@@ -1,23 +1,26 @@
 /* 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 ALL_SIDES, maybe_moz_logical_alias %>
 <% data.new_style_struct("Padding", inherited=False) %>
 
+// APPLIES_TO_PLACEHOLDER so we can set it in UA  stylesheets.  But we use a
+// !important value there, so pages can't set it.
 % for side in ALL_SIDES:
     <%
         spec = "https://drafts.csswg.org/css-box/#propdef-padding-%s" % side[0]
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-%s" % side[1]
     %>
     ${helpers.predefined_type("padding-%s" % side[0], "LengthOrPercentage",
                               "computed::LengthOrPercentage::Length(Au(0))",
                               "parse_non_negative",
                               alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
                               animation_value_type="ComputedValue",
                               logical = side[1],
                               spec = spec,
+                              flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_PLACEHOLDER",
                               allow_quirks=not side[1])}
 % endfor
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -147,16 +147,17 @@
     }
 </%helpers:longhand>
 
 // NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
 // is nonstandard, slated for CSS4-UI.
 // TODO(pcwalton): SVG-only values.
 ${helpers.single_keyword("pointer-events", "auto none", animation_value_type="discrete",
                          extra_gecko_values="visiblepainted visiblefill visiblestroke visible painted fill stroke all",
+                         flags="APPLIES_TO_PLACEHOLDER",
                          spec="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty")}
 
 ${helpers.single_keyword("-moz-user-input", "auto none enabled disabled",
                          products="gecko", gecko_ffi_name="mUserInput",
                          gecko_enum_prefix="StyleUserInput",
                          gecko_inexhaustive=True,
                          animation_value_type="discrete",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)")}
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -8,16 +8,17 @@
 <% data.new_style_struct("Text",
                          inherited=False,
                          gecko_name="TextReset",
                          additional_methods=[Method("has_underline", "bool"),
                                              Method("has_overline", "bool"),
                                              Method("has_line_through", "bool")]) %>
 
 <%helpers:longhand name="text-overflow" animation_value_type="discrete" boxed="True"
+                   flags="APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow">
     use std::fmt;
     use style_traits::ToCss;
 
     no_viewport_percentage!(SpecifiedValue);
 
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
@@ -137,16 +138,17 @@
                          animation_value_type="discrete",
                          need_clone="True",
                          spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi")}
 
 <%helpers:longhand name="text-decoration-line"
                    custom_cascade="${product == 'servo'}"
                    need_clone=True
                    animation_value_type="discrete"
+                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
@@ -257,27 +259,30 @@
     #[cfg(feature = "gecko")]
     impl_bitflags_conversions!(SpecifiedValue);
 </%helpers:longhand>
 
 ${helpers.single_keyword("text-decoration-style",
                          "solid double dotted dashed wavy -moz-none",
                          products="gecko",
                          animation_value_type="discrete",
+                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                          spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}
 
 ${helpers.predefined_type(
     "text-decoration-color", "Color",
     "computed_value::T::currentcolor()",
     initial_specified_value="specified::Color::currentcolor()",
     products="gecko",
     animation_value_type="IntermediateColor",
     ignored_when_colors_disabled=True,
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
 
 ${helpers.predefined_type(
     "initial-letter",
     "InitialLetter",
     "computed::InitialLetter::normal()",
     initial_specified_value="specified::InitialLetter::normal()",
     animation_value_type="discrete",
     products="gecko",
+    flags="APPLIES_TO_FIRST_LETTER",
     spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials")}
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -437,16 +437,22 @@ bitflags! {
         /// This property has values that can establish a containing block for
         /// fixed positioned and absolutely positioned elements.
         const FIXPOS_CB = 1 << 1,
         /// This property has values that can establish a containing block for
         /// absolutely positioned elements.
         const ABSPOS_CB = 1 << 2,
         /// This shorthand property is an alias of another property.
         const SHORTHAND_ALIAS_PROPERTY = 1 << 3,
+        /// This longhand property applies to ::first-letter.
+        const APPLIES_TO_FIRST_LETTER = 1 << 4,
+        /// This longhand property applies to ::first-line.
+        const APPLIES_TO_FIRST_LINE = 1 << 5,
+        /// This longhand property applies to ::placeholder.
+        const APPLIES_TO_PLACEHOLDER = 1 << 6,
     }
 }
 
 /// An identifier for a given longhand property.
 #[derive(Clone, Copy, Eq, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum LonghandId {
     % for i, property in enumerate(data.longhands):
@@ -2861,21 +2867,36 @@ pub fn cascade(
             let source = node.style_source();
             let declarations = if source.is_some() {
                 source.read(cascade_level.guard(guards)).declarations()
             } else {
                 // The root node has no style source.
                 &[]
             };
             let node_importance = node.importance();
+
+            let property_restriction = pseudo.and_then(|p| p.property_restriction());
+
             declarations
                 .iter()
                 // Yield declarations later in source order (with more precedence) first.
                 .rev()
                 .filter_map(move |&(ref declaration, declaration_importance)| {
+                    if let Some(property_restriction) = property_restriction {
+                        // declaration.id() is either a longhand or a custom
+                        // property.  Custom properties are always allowed, but
+                        // longhands are only allowed if they have our
+                        // property_restriction flag set.
+                        if let PropertyDeclarationId::Longhand(id) = declaration.id() {
+                            if !id.flags().contains(property_restriction) {
+                                return None
+                            }
+                        }
+                    }
+
                     if declaration_importance == node_importance {
                         Some((declaration, cascade_level))
                     } else {
                         None
                     }
                 })
         })
     };
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -8,16 +8,17 @@
 
 use {Atom, Prefix, Namespace, LocalName, CaseSensitivityExt};
 use attr::{AttrIdentifier, AttrValue};
 use cssparser::{Parser as CssParser, ToCss, serialize_identifier, CompactCowStr};
 use dom::{OpaqueNode, TElement, TNode};
 use element_state::ElementState;
 use fnv::FnvHashMap;
 use invalidation::element::element_wrapper::ElementSnapshot;
+use properties::PropertyFlags;
 use selector_parser::{AttrValue as SelectorAttrValue, ElementExt, PseudoElementCascadeType, SelectorParser};
 use selectors::Element;
 use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
 use selectors::parser::{SelectorMethods, SelectorParseError};
 use selectors::visitor::SelectorVisitor;
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::fmt;
@@ -34,16 +35,22 @@ use style_traits::{ParseError, StylePars
 #[allow(missing_docs)]
 #[repr(usize)]
 pub enum PseudoElement {
     // Eager pseudos. Keep these first so that eager_index() works.
     After = 0,
     Before,
     Selection,
     // If/when :first-letter is added, update is_first_letter accordingly.
+
+    // If/when ::first-letter, ::first-line, or ::placeholder are added, adjust
+    // our property_restriction implementation to do property filtering for
+    // them.  Also, make sure the UA sheet has the !important rules some of the
+    // APPLIES_TO_PLACEHOLDER properties expect!
+
     // Non-eager pseudos.
     DetailsSummary,
     DetailsContent,
     ServoText,
     ServoInputText,
     ServoTableWrapper,
     ServoAnonymousTableWrapper,
     ServoAnonymousTable,
@@ -164,16 +171,22 @@ impl PseudoElement {
     /// Covert non-canonical pseudo-element to canonical one, and keep a
     /// canonical one as it is.
     pub fn canonical(&self) -> PseudoElement {
         self.clone()
     }
 
     /// Stub, only Gecko needs this
     pub fn pseudo_info(&self) { () }
+
+    /// Property flag that properties must have to apply to this pseudo-element.
+    #[inline]
+    pub fn property_restriction(&self) -> Option<PropertyFlags> {
+        None
+    }
 }
 
 /// The type used for storing pseudo-class string arguments.
 pub type PseudoClassStringArg = Box<str>;
 
 /// A non tree-structural pseudo-class.
 /// See https://drafts.csswg.org/selectors-4/#structural-pseudos
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]