servo: Merge #17092 - style: Add support for disabled document colors (from heycam:document-colors); r=xidorn
authorCameron McCormack <cam@mcc.id.au>
Tue, 30 May 2017 06:10:42 -0500
changeset 361331 d1deb3611dfceb9ce82e8c295a581e05b466e104
parent 361330 b3f99cbb232819060a6643e4c1430361d71a6b9c
child 361332 db14c0e392d03d81a229bc2e68325f27d6cf0e41
push id90835
push userryanvm@gmail.com
push dateTue, 30 May 2017 20:09:55 +0000
treeherdermozilla-inbound@efea61490537 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersxidorn
milestone55.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 #17092 - style: Add support for disabled document colors (from heycam:document-colors); r=xidorn Reviewed in https://bugzilla.mozilla.org/show_bug.cgi?id=1355716. Source-Repo: https://github.com/servo/servo Source-Revision: 43862ba0450427c7ad27e191789df5cf6d746c55
servo/components/style/animation.rs
servo/components/style/gecko/media_queries.rs
servo/components/style/properties/data.py
servo/components/style/properties/longhand/background.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/color.mako.rs
servo/components/style/properties/longhand/column.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/outline.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/media_queries.rs
servo/components/style/values/specified/mod.rs
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -14,16 +14,17 @@ use font_metrics::FontMetricsProvider;
 use keyframes::{KeyframesStep, KeyframesStepValue};
 use properties::{self, CascadeFlags, ComputedValues, Importance};
 use properties::animated_properties::{AnimatedProperty, TransitionProperty};
 use properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
 use properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount;
 use properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
 use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd;
 use properties::longhands::transition_timing_function::single_value::computed_value::T as TransitionTimingFunction;
+use rule_tree::CascadeLevel;
 use std::sync::mpsc::Sender;
 use stylearc::Arc;
 use timer::Timer;
 use values::computed::Time;
 
 /// This structure represents a keyframes animation current iteration state.
 ///
 /// If the iteration count is infinite, there's no other state, otherwise we
@@ -467,17 +468,18 @@ fn compute_style_for_animation_step(cont
         KeyframesStepValue::Declarations { block: ref declarations } => {
             let guard = declarations.read_with(context.guards.author);
 
             // No !important in keyframes.
             debug_assert!(guard.declarations().iter()
                             .all(|&(_, importance)| importance == Importance::Normal));
 
             let iter = || {
-                guard.declarations().iter().rev().map(|&(ref decl, _importance)| decl)
+                guard.declarations().iter().rev()
+                     .map(|&(ref decl, _importance)| (decl, CascadeLevel::Animations))
             };
 
             // This currently ignores visited styles, which seems acceptable,
             // as existing browsers don't appear to animate visited styles.
             let computed =
                 properties::apply_declarations(context.stylist.device(),
                                                /* is_root = */ false,
                                                iter,
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -1,19 +1,20 @@
 /* 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/. */
 
 //! Gecko's media-query device and expression representation.
 
 use app_units::Au;
 use context::QuirksMode;
-use cssparser::{CssStringWriter, Parser, Token};
+use cssparser::{CssStringWriter, Parser, RGBA, Token};
 use euclid::Size2D;
 use font_metrics::get_metrics_provider_for_product;
+use gecko::values::convert_nscolor_to_rgba;
 use gecko_bindings::bindings;
 use gecko_bindings::structs::{nsCSSKeyword, nsCSSProps_KTableEntry, nsCSSValue, nsCSSUnit, nsStringBuffer};
 use gecko_bindings::structs::{nsMediaExpression_Range, nsMediaFeature};
 use gecko_bindings::structs::{nsMediaFeature_ValueType, nsMediaFeature_RangeType, nsMediaFeature_RequirementFlags};
 use gecko_bindings::structs::RawGeckoPresContextOwned;
 use media_queries::MediaType;
 use parser::ParserContext;
 use properties::{ComputedValues, StyleBuilder};
@@ -129,16 +130,26 @@ impl Device {
             Size2D::new(Au::from_f32_px(v.size.width),
                         Au::from_f32_px(v.size.height))
         }).unwrap_or_else(|| unsafe {
             // TODO(emilio): Need to take into account scrollbars.
             Size2D::new(Au((*self.pres_context).mVisibleArea.width),
                         Au((*self.pres_context).mVisibleArea.height))
         })
     }
+
+    /// Returns whether document colors are enabled.
+    pub fn use_document_colors(&self) -> bool {
+        unsafe { (*self.pres_context).mUseDocumentColors() != 0 }
+    }
+
+    /// Returns the default background color.
+    pub fn default_background_color(&self) -> RGBA {
+        convert_nscolor_to_rgba(unsafe { (*self.pres_context).mBackgroundColor })
+    }
 }
 
 /// A expression for gecko contains a reference to the media feature, the value
 /// the media query contained, and the range to evaluate.
 #[derive(Debug, Clone)]
 pub struct Expression {
     feature: &'static nsMediaFeature,
     value: Option<MediaExpressionValue>,
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -145,17 +145,18 @@ def arg_to_bool(arg):
 
 
 class Longhand(object):
     def __init__(self, style_struct, name, spec=None, animation_value_type=None, derived_from=None, keyword=None,
                  predefined_type=None, custom_cascade=False, experimental=False, internal=False,
                  need_clone=False, need_index=False, gecko_ffi_name=None, depend_on_viewport_size=False,
                  allowed_in_keyframe_block=True, complex_color=False, cast_type='u8',
                  has_uncacheable_values=False, logical=False, alias=None, extra_prefixes=None, boxed=False,
-                 flags=None, allowed_in_page_rule=False, allow_quirks=False, vector=False):
+                 flags=None, allowed_in_page_rule=False, allow_quirks=False, ignored_when_colors_disabled=False,
+                 vector=False):
         self.name = name
         if not spec:
             raise TypeError("Spec should be specified for %s" % name)
         self.spec = spec
         self.keyword = keyword
         self.predefined_type = predefined_type
         self.ident = to_rust_ident(name)
         self.camel_case = to_camel_case(self.ident)
@@ -172,16 +173,17 @@ class Longhand(object):
         self.cast_type = cast_type
         self.logical = arg_to_bool(logical)
         self.alias = alias.split() if alias else []
         self.extra_prefixes = extra_prefixes.split() if extra_prefixes else []
         self.boxed = arg_to_bool(boxed)
         self.flags = flags.split() if flags else []
         self.allowed_in_page_rule = arg_to_bool(allowed_in_page_rule)
         self.allow_quirks = allow_quirks
+        self.ignored_when_colors_disabled = ignored_when_colors_disabled
         self.is_vector = vector
 
         # https://drafts.csswg.org/css-animations/#keyframes
         # > The <declaration-list> inside of <keyframe-block> accepts any CSS property
         # > except those defined in this specification,
         # > but does accept the `animation-play-state` property and interprets it specially.
         self.allowed_in_keyframe_block = allowed_in_keyframe_block \
             and allowed_in_keyframe_block != "False"
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -7,25 +7,27 @@
 <% data.new_style_struct("Background", inherited=False) %>
 
 ${helpers.predefined_type("background-color", "CSSColor",
     "::cssparser::Color::RGBA(::cssparser::RGBA::transparent())",
     initial_specified_value="SpecifiedValue::transparent()",
     spec="https://drafts.csswg.org/css-backgrounds/#background-color",
     animation_value_type="IntermediateColor",
     complex_color=True,
+    ignored_when_colors_disabled=True,
     allow_quirks=True)}
 
 ${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="none",
-    has_uncacheable_values="True" if product == "gecko" else "False")}
+    has_uncacheable_values="True" if product == "gecko" else "False",
+    ignored_when_colors_disabled="True")}
 
 % 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, delegate_animate=True)}
 % endfor
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -17,17 +17,18 @@
 %>
 % for side in ALL_SIDES:
     ${helpers.predefined_type("border-%s-color" % side[0], "CSSColor",
                               "::cssparser::Color::CurrentColor",
                               alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
                               spec=maybe_logical_spec(side, "color"),
                               animation_value_type="IntermediateColor",
                               logical=side[1],
-                              allow_quirks=not side[1])}
+                              allow_quirks=not side[1],
+                              ignored_when_colors_disabled=True)}
 
     ${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"),
                               animation_value_type="none", logical=side[1])}
 
@@ -54,17 +55,18 @@
                               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="none"
                        spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-border-*-colors)"
-                       products="gecko">
+                       products="gecko"
+                       ignored_when_colors_disabled="True">
         use std::fmt;
         use style_traits::ToCss;
         use values::specified::CSSColor;
         no_viewport_percentage!(SpecifiedValue);
 
         pub mod computed_value {
             use values::computed::CSSColor;
             #[derive(Debug, Clone, PartialEq)]
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -5,16 +5,17 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% 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"
                    spec="https://drafts.csswg.org/css-color/#color">
     use cssparser::RGBA;
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::{AllowQuirks, Color, CSSColor};
 
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
--- a/servo/components/style/properties/longhand/column.mako.rs
+++ b/servo/components/style/properties/longhand/column.mako.rs
@@ -47,16 +47,17 @@
                           animation_value_type="ComputedValue", extra_prefixes="moz")}
 
 // https://drafts.csswg.org/css-multicol-1/#crc
 ${helpers.predefined_type("column-rule-color", "CSSColor",
                           "::cssparser::Color::CurrentColor",
                           initial_specified_value="specified::CSSColor::currentcolor()",
                           products="gecko", animation_value_type="IntermediateColor", extra_prefixes="moz",
                           complex_color=True, need_clone=True,
+                          ignored_when_colors_disabled=True,
                           spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color")}
 
 ${helpers.single_keyword("column-span", "none all",
                          products="gecko", animation_value_type="discrete",
                          spec="https://drafts.csswg.org/css-multicol/#propdef-column-span")}
 
 ${helpers.single_keyword("column-rule-style",
                          "none hidden dotted dashed solid double groove ridge inset outset",
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -12,16 +12,17 @@
                           "1.0",
                           animation_value_type="ComputedValue",
                           flags="CREATES_STACKING_CONTEXT",
                           spec="https://drafts.csswg.org/css-color/#opacity")}
 
 <%helpers:vector_longhand name="box-shadow" allow_empty="True"
                           animation_value_type="IntermediateBoxShadowList"
                           extra_prefixes="webkit"
+                          ignored_when_colors_disabled="True"
                           spec="https://drafts.csswg.org/css-backgrounds/#box-shadow">
     use std::fmt;
     use style_traits::ToCss;
 
     pub type SpecifiedValue = specified::Shadow;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -675,16 +675,17 @@
             }
         }
     }
     % endif
 </%helpers:single_keyword_computed>
 
 <%helpers:longhand name="text-shadow"
                    animation_value_type="IntermediateTextShadowList",
+                   ignored_when_colors_disabled="True",
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-shadow">
     use cssparser;
     use std::fmt;
     use style_traits::ToCss;
     use values::specified::Shadow;
 
     #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -1112,16 +1113,17 @@
     % endif
 </%helpers:longhand>
 
 ${helpers.predefined_type("text-emphasis-color", "CSSColor",
                           "::cssparser::Color::CurrentColor",
                           initial_specified_value="specified::CSSColor::currentcolor()",
                           products="gecko", animation_value_type="IntermediateColor",
                           complex_color=True, need_clone=True,
+                          ignored_when_colors_disabled=True,
                           spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color")}
 
 
 ${helpers.predefined_type(
     "-moz-tab-size", "LengthOrNumber",
     "::values::Either::Second(8.0)",
     "parse_non_negative",
     products="gecko", animation_value_type="none",
@@ -1130,24 +1132,26 @@
 
 // CSS Compatibility
 // https://compat.spec.whatwg.org
 ${helpers.predefined_type(
     "-webkit-text-fill-color", "CSSColor",
     "CSSParserColor::CurrentColor",
     products="gecko", animation_value_type="IntermediateColor",
     complex_color=True, need_clone=True,
+    ignored_when_colors_disabled=True,
     spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color")}
 
 ${helpers.predefined_type(
     "-webkit-text-stroke-color", "CSSColor",
     "CSSParserColor::CurrentColor",
     initial_specified_value="specified::CSSColor::currentcolor()",
     products="gecko", animation_value_type="IntermediateColor",
     complex_color=True, need_clone=True,
+    ignored_when_colors_disabled=True,
     spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color")}
 
 ${helpers.predefined_type("-webkit-text-stroke-width", "BorderWidth", "Au::from_px(0)",
                           initial_specified_value="specified::BorderWidth::from_length(specified::Length::zero())",
                           computed_type="::app_units::Au", products="gecko",
                           spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width",
                           animation_value_type="none")}
 
--- a/servo/components/style/properties/longhand/outline.mako.rs
+++ b/servo/components/style/properties/longhand/outline.mako.rs
@@ -8,16 +8,17 @@
 <% data.new_style_struct("Outline",
                          inherited=False,
                          additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
 
 // TODO(pcwalton): `invert`
 ${helpers.predefined_type("outline-color", "CSSColor", "computed::CSSColor::CurrentColor",
                           initial_specified_value="specified::CSSColor::currentcolor()",
                           animation_value_type="IntermediateColor", complex_color=True, need_clone=True,
+                          ignored_when_colors_disabled=True,
                           spec="https://drafts.csswg.org/css-ui/#propdef-outline-color")}
 
 <%helpers:longhand name="outline-style" need_clone="True" animation_value_type="none"
                    spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
     use values::specified::BorderStyle;
 
     pub type SpecifiedValue = Either<Auto, BorderStyle>;
 
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -179,9 +179,10 @@
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)")}
 
 ${helpers.predefined_type("caret-color",
                           "ColorOrAuto",
                           "Either::Second(Auto)",
                           spec="https://drafts.csswg.org/css-ui/#caret-color",
                           animation_value_type="Either<IntermediateColor, Auto>",
                           boxed=True,
+                          ignored_when_colors_disabled=True,
                           products="gecko")}
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -279,16 +279,17 @@
 
 ${helpers.predefined_type(
     "text-decoration-color", "CSSColor",
     "computed::CSSColor::CurrentColor",
     initial_specified_value="specified::CSSColor::currentcolor()",
     complex_color=True,
     products="gecko",
     animation_value_type="IntermediateColor",
+    ignored_when_colors_disabled=True,
     spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
 
 <%helpers:longhand name="initial-letter"
                    animation_value_type="none"
                    products="gecko"
                    spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials">
     use std::fmt;
     use style_traits::ToCss;
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -32,19 +32,20 @@ use logical_geometry::WritingMode;
 use media_queries::Device;
 use parser::{PARSING_MODE_DEFAULT, Parse, ParserContext};
 use properties::animated_properties::TransitionProperty;
 #[cfg(feature = "servo")] use servo_config::prefs::PREFS;
 use shared_lock::StylesheetGuards;
 use style_traits::{HasViewportPercentage, ToCss};
 use stylesheets::{CssRuleType, MallocSizeOf, MallocSizeOfFn, Origin, UrlExtraData};
 #[cfg(feature = "servo")] use values::Either;
+use values::specified::Color;
 use values::computed;
 use cascade_info::CascadeInfo;
-use rule_tree::StrongRuleNode;
+use rule_tree::{CascadeLevel, StrongRuleNode};
 use style_adjuster::StyleAdjuster;
 #[cfg(feature = "servo")] use values::specified::BorderStyle;
 
 pub use self::declaration_block::*;
 
 #[cfg(feature = "gecko")]
 #[macro_export]
 macro_rules! property_name {
@@ -586,16 +587,25 @@ impl LonghandId {
             LonghandId::BorderTopColor |
             LonghandId::BorderRightColor |
             LonghandId::BorderBottomColor |
             LonghandId::BorderLeftColor |
             LonghandId::OutlineColor
         )
     }
 
+    /// Returns true if the property is one that is ignored when document
+    /// colors are disabled.
+    fn is_ignored_when_document_colors_disabled(&self) -> bool {
+        matches!(*self,
+            ${" | ".join([("LonghandId::" + p.camel_case)
+                          for p in data.longhands if p.ignored_when_colors_disabled])}
+        )
+    }
+
     /// The computed value of some properties depends on the (sometimes
     /// computed) value of *other* properties.
     ///
     /// So we classify properties into "early" and "other", such that the only
     /// dependencies can be from "other" to "early".
     ///
     /// Unfortunately, it’s not easy to check that this classification is
     /// correct.
@@ -2494,29 +2504,30 @@ pub fn cascade(device: &Device,
             (true,
              device.default_computed_values(),
              device.default_computed_values())
         }
     };
 
     let iter_declarations = || {
         rule_node.self_and_ancestors().flat_map(|node| {
+            let cascade_level = node.cascade_level();
             let declarations = match node.style_source() {
-                Some(source) => source.read(node.cascade_level().guard(guards)).declarations(),
+                Some(source) => source.read(cascade_level.guard(guards)).declarations(),
                 // The root node has no style source.
                 None => &[]
             };
             let node_importance = node.importance();
             declarations
                 .iter()
                 // Yield declarations later in source order (with more precedence) first.
                 .rev()
                 .filter_map(move |&(ref declaration, declaration_importance)| {
                     if declaration_importance == node_importance {
-                        Some(declaration)
+                        Some((declaration, cascade_level))
                     } else {
                         None
                     }
                 })
         })
     };
     apply_declarations(device,
                        is_root_element,
@@ -2542,23 +2553,23 @@ pub fn apply_declarations<'a, F, I>(devi
                                     visited_style: Option<Arc<ComputedValues>>,
                                     mut cascade_info: Option<<&mut CascadeInfo>,
                                     error_reporter: &ParseErrorReporter,
                                     font_metrics_provider: &FontMetricsProvider,
                                     flags: CascadeFlags,
                                     quirks_mode: QuirksMode)
                                     -> ComputedValues
     where F: Fn() -> I,
-          I: Iterator<Item = &'a PropertyDeclaration>,
+          I: Iterator<Item = (&'a PropertyDeclaration, CascadeLevel)>,
 {
     let default_style = device.default_computed_values();
     let inherited_custom_properties = inherited_style.custom_properties();
     let mut custom_properties = None;
     let mut seen_custom = HashSet::new();
-    for declaration in iter_declarations() {
+    for (declaration, _cascade_level) in iter_declarations() {
         if let PropertyDeclaration::Custom(ref name, ref value) = *declaration {
             ::custom_properties::cascade(
                 &mut custom_properties, &inherited_custom_properties,
                 &mut seen_custom, name, value.borrow());
         }
     }
 
     let custom_properties =
@@ -2596,16 +2607,24 @@ pub fn apply_declarations<'a, F, I>(devi
         layout_parent_style: layout_parent_style,
         style: builder,
         font_metrics_provider: font_metrics_provider,
         cached_system_font: None,
         in_media_query: false,
         quirks_mode: quirks_mode,
     };
 
+    let ignore_colors = !device.use_document_colors();
+    let default_background_color_decl = if ignore_colors {
+        let color = device.default_background_color();
+        Some(PropertyDeclaration::BackgroundColor(Color::RGBA(color).into()))
+    } else {
+        None
+    };
+
     // Set computed values, overwriting earlier declarations for the same
     // property.
     //
     // NB: The cacheable boolean is not used right now, but will be once we
     // start caching computed values in the rule nodes.
     let mut cacheable = true;
     let mut seen = LonghandIdSet::new();
 
@@ -2620,30 +2639,53 @@ pub fn apply_declarations<'a, F, I>(devi
     % for category_to_cascade_now in ["early", "other"]:
         % if category_to_cascade_now == "early":
             // Pull these out so that we can
             // compute them in a specific order without
             // introducing more iterations
             let mut font_size = None;
             let mut font_family = None;
         % endif
-        for declaration in iter_declarations() {
+        for (declaration, cascade_level) in iter_declarations() {
+            let mut declaration = declaration;
             let longhand_id = match declaration.id() {
                 PropertyDeclarationId::Longhand(id) => id,
                 PropertyDeclarationId::Custom(..) => continue,
             };
 
             // Only a few properties are allowed to depend on the visited state
             // of links.  When cascading visited styles, we can save time by
             // only processing these properties.
             if flags.contains(VISITED_DEPENDENT_ONLY) &&
                !longhand_id.is_visited_dependent() {
                 continue
             }
 
+            // When document colors are disabled, skip properties that are
+            // marked as ignored in that mode, if they come from a UA or
+            // user style sheet.
+            if ignore_colors &&
+               longhand_id.is_ignored_when_document_colors_disabled() &&
+               !matches!(cascade_level,
+                         CascadeLevel::UANormal |
+                         CascadeLevel::UserNormal |
+                         CascadeLevel::UserImportant |
+                         CascadeLevel::UAImportant) {
+                if let PropertyDeclaration::BackgroundColor(ref color) = *declaration {
+                    // Treat background-color a bit differently.  If the specified
+                    // color is anything other than a fully transparent color, convert
+                    // it into the Device's default background color.
+                    if color.is_non_transparent() {
+                        declaration = default_background_color_decl.as_ref().unwrap();
+                    }
+                } else {
+                    continue
+                }
+            }
+
             if
                 % if category_to_cascade_now == "early":
                     !
                 % endif
                 longhand_id.is_early_property()
             {
                 continue
             }
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -1,17 +1,17 @@
 /* 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/. */
 
 //! Servo's media-query device and expression representation.
 
 use app_units::Au;
 use context::QuirksMode;
-use cssparser::Parser;
+use cssparser::{Parser, RGBA};
 use euclid::{Size2D, TypedSize2D};
 use font_metrics::ServoMetricsProvider;
 use media_queries::MediaType;
 use parser::ParserContext;
 use properties::{ComputedValues, StyleBuilder};
 use properties::longhands::font_size;
 use std::fmt;
 use std::sync::atomic::{AtomicIsize, Ordering};
@@ -91,16 +91,26 @@ impl Device {
     pub fn account_for_viewport_rule(&mut self, constraints: &ViewportConstraints) {
         self.viewport_size = constraints.size;
     }
 
     /// Return the media type of the current device.
     pub fn media_type(&self) -> MediaType {
         self.media_type.clone()
     }
+
+    /// Returns whether document colors are enabled.
+    pub fn use_document_colors(&self) -> bool {
+        true
+    }
+
+    /// Returns the default background color.
+    pub fn default_background_color(&self) -> RGBA {
+        RGBA::new(255, 255, 255, 255)
+    }
 }
 
 /// A expression kind servo understands and parses.
 ///
 /// Only `pub` for unit testing, please don't use it directly!
 #[derive(PartialEq, Clone, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum ExpressionKind {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -172,16 +172,25 @@ impl CSSColor {
             written += (&mut serialization[written..]).write(dimension.as_bytes()).unwrap();
         }
         debug_assert!(written == 6);
         Ok(CSSColor {
             parsed: cssparser::Color::parse_hash(&serialization).map(From::from)?,
             authored: None,
         })
     }
+
+    /// Returns false if the color is completely transparent, and
+    /// true otherwise.
+    pub fn is_non_transparent(&self) -> bool {
+        match self.parsed {
+            Color::RGBA(rgba) if rgba.alpha == 0 => false,
+            _ => true,
+        }
+    }
 }
 
 no_viewport_percentage!(CSSColor);
 
 impl ToCss for CSSColor {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match self.authored {
             Some(ref s) => dest.write_str(s),