Merge mozilla-central to autoland. a=merge CLOSED TREE
authorBrindusan Cristian <cbrindusan@mozilla.com>
Sat, 09 Jun 2018 01:01:52 +0300
changeset 478717 437d495c93b657420be63d179b420f956050d4c9
parent 478716 c77f7fe4c21b50dc7525602f9b147530e89c2e8b (current diff)
parent 478686 ac8a73f9dbb79f890176cdaaf4b044f51de364a2 (diff)
child 478718 8a278eebbe5df459eab42bc7f1f515cbc8e38a01
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.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
Merge mozilla-central to autoland. a=merge CLOSED TREE
--- a/devtools/client/aboutdebugging/test/browser.ini
+++ b/devtools/client/aboutdebugging/test/browser.ini
@@ -27,17 +27,17 @@ skip-if = coverage # Bug 1387827
 [browser_addons_debug_info.js]
 [browser_addons_debug_webextension.js]
 tags = webextensions
 [browser_addons_debug_webextension_inspector.js]
 tags = webextensions
 [browser_addons_debug_webextension_nobg.js]
 tags = webextensions
 [browser_addons_debug_webextension_popup.js]
-skip-if = coverage || (verify && debug) # coverage: Bug 1387827, verify: crashes on shutdown
+skip-if = coverage || (verify && debug) || (debug && os == "linux" && bits == 64) # coverage: Bug 1387827, verify: crashes on shutdown, timeouts linux debug Bug 1299001
 tags = webextensions
 [browser_addons_debugging_initial_state.js]
 [browser_addons_install.js]
 [browser_addons_reload.js]
 [browser_addons_remove.js]
 [browser_addons_toggle_debug.js]
 [browser_page_not_found.js]
 [browser_service_workers.js]
--- a/layout/style/CSSPropFlags.h
+++ b/layout/style/CSSPropFlags.h
@@ -44,15 +44,18 @@ enum class CSSPropFlags : uint8_t
 
   // This property can be animated on the compositor.
   CanAnimateOnCompositor = 1 << 4,
 
   // This property is an internal property that is not represented in
   // the DOM. Properties with this flag are defined in an #ifndef
   // CSS_PROP_LIST_EXCLUDE_INTERNAL section.
   Internal = 1 << 5,
+
+  // Whether this property should be serialized by Servo in getComputedStyle.
+  SerializedByServo = 1 << 6,
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CSSPropFlags)
 
 } // namespace mozilla
 
 #endif // mozilla_CSSPropFlags_h
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -820,28 +820,31 @@ SERVO_BINDING_FUNC(Servo_StyleSet_GetCom
                    const mozilla::ServoElementSnapshotTable* snapshots,
                    RawServoAnimationValueBorrowed animation)
 
 // For canvas font.
 SERVO_BINDING_FUNC(Servo_SerializeFontValueForCanvas, void,
                    RawServoDeclarationBlockBorrowed declarations,
                    nsAString* buffer)
 
-// Get custom property value.
+// GetComputedStyle APIs.
 SERVO_BINDING_FUNC(Servo_GetCustomPropertyValue, bool,
                    ComputedStyleBorrowed computed_values,
                    const nsAString* name, nsAString* value)
 
 SERVO_BINDING_FUNC(Servo_GetCustomPropertiesCount, uint32_t,
                    ComputedStyleBorrowed computed_values)
 
 SERVO_BINDING_FUNC(Servo_GetCustomPropertyNameAt, bool,
                    ComputedStyleBorrowed, uint32_t index,
                    nsAString* name)
 
+SERVO_BINDING_FUNC(Servo_GetPropertyValue, void,
+                   ComputedStyleBorrowed computed_values,
+                   nsCSSPropertyID property, nsAString* value)
 
 SERVO_BINDING_FUNC(Servo_ProcessInvalidations, void,
                    RawServoStyleSetBorrowed set,
                    RawGeckoElementBorrowed element,
                    const mozilla::ServoElementSnapshotTable* snapshots)
 
 
 SERVO_BINDING_FUNC(Servo_HasPendingRestyleAncestor, bool,
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -420,16 +420,17 @@ mapped-generic-types = [
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
     { generic = false, gecko = "mozilla::ServoVisitedStyle", servo = "Option<::servo_arc::RawOffsetArc<::properties::ComputedValues>>" },
     { generic = false, gecko = "mozilla::ServoComputedValueFlags", servo = "::properties::computed_value_flags::ComputedValueFlags" },
     { generic = true, gecko = "mozilla::ServoRawOffsetArc", servo = "::servo_arc::RawOffsetArc" },
     { generic = false, gecko = "ComputedStyleStrong", servo = "::gecko_bindings::sugar::ownership::Strong<::properties::ComputedValues>" },
+    { generic = false, gecko = "RawServoUnlockedDeclarationBlock", servo = "::properties::PropertyDeclarationBlock" },
 ]
 fixups = [
     { pat = "\\broot\\s*::\\s*nsString\\b", rep = "::nsstring::nsStringRepr" },
     { pat = "\\broot\\s*::\\s*nsTString\\s*<\\s*u16\\s*>", rep = "::nsstring::nsStringRepr" },
 ]
 
 [bindings]
 headers = ["mozilla/ServoBindings.h"]
--- a/layout/style/ServoCSSPropList.mako.py
+++ b/layout/style/ServoCSSPropList.mako.py
@@ -63,30 +63,60 @@ def is_internal(prop):
 
 def method(prop):
     if prop.name == "float":
         return "CssFloat"
     if prop.name.startswith("-x-"):
         return prop.camel_case[1:]
     return prop.camel_case
 
+# Colors, integers and lengths are easy as well.
+#
+# TODO(emilio): This will go away once the rest of the longhands have been
+# moved or perhaps using a blacklist for the ones with non-layout-dependence
+# but other non-trivial dependence like scrollbar colors.
+SERIALIZED_PREDEFINED_TYPES = [
+    "Color",
+    "Integer",
+    "Length",
+    "Opacity",
+]
+
+def serialized_by_servo(prop):
+    # If the property requires layout information, no such luck.
+    if "GETCS_NEEDS_LAYOUT_FLUSH" in prop.flags:
+        return False
+    # No shorthands yet.
+    if prop.type() == "shorthand":
+        return False
+    # Keywords are all fine.
+    if prop.keyword:
+        return True
+    if prop.predefined_type in SERIALIZED_PREDEFINED_TYPES:
+        return True
+    # TODO(emilio): Enable the rest of the longhands.
+    return False
+
+
 def flags(prop):
     result = []
     if prop.explicitly_enabled_in_chrome():
         result.append("EnabledInUASheetsAndChrome")
     elif prop.explicitly_enabled_in_ua_sheets():
         result.append("EnabledInUASheets")
     if is_internal(prop):
         result.append("Internal")
     if prop.enabled_in == "":
         result.append("Inaccessible")
     if "GETCS_NEEDS_LAYOUT_FLUSH" in prop.flags:
         result.append("GetCSNeedsLayoutFlush")
     if "CAN_ANIMATE_ON_COMPOSITOR" in prop.flags:
         result.append("CanAnimateOnCompositor")
+    if serialized_by_servo(prop):
+        result.append("SerializedByServo")
     return ", ".join('"CSSPropFlags::{}"'.format(flag) for flag in result)
 
 def pref(prop):
     if prop.gecko_pref:
         return '"' + prop.gecko_pref + '"'
     return '""'
 
 def sub_properties(prop):
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -430,30 +430,57 @@ nsComputedDOMStyle::GetParentRule()
 }
 
 NS_IMETHODIMP
 nsComputedDOMStyle::GetPropertyValue(const nsAString& aPropertyName,
                                      nsAString& aReturn)
 {
   aReturn.Truncate();
 
-  ErrorResult error;
-  RefPtr<CSSValue> val =
-    GetPropertyCSSValueWithoutWarning(aPropertyName, error);
-  if (error.Failed()) {
-    return error.StealNSResult();
-  }
-
-  if (val) {
-    nsString text;
-    val->GetCssText(text, error);
-    aReturn.Assign(text);
-    return error.StealNSResult();
-  }
-
+  nsCSSPropertyID prop =
+    nsCSSProps::LookupProperty(aPropertyName, CSSEnabledState::eForAllContent);
+
+  const ComputedStyleMap::Entry* entry = nullptr;
+  if (prop != eCSSPropertyExtra_variable) {
+    entry = GetComputedStyleMap()->FindEntryForProperty(prop);
+    if (!entry) {
+      return NS_OK;
+    }
+  }
+
+  const bool layoutFlushIsNeeded = entry && entry->IsLayoutFlushNeeded();
+  UpdateCurrentStyleSources(layoutFlushIsNeeded);
+  if (!mComputedStyle) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  auto cleanup = mozilla::MakeScopeExit([&] {
+    ClearCurrentStyleSources();
+  });
+
+  if (!entry) {
+    MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
+    const nsAString& name =
+      Substring(aPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH);
+    Servo_GetCustomPropertyValue(mComputedStyle, &name, &aReturn);
+    return NS_OK;
+  }
+
+  if (!nsCSSProps::PropHasFlags(prop, CSSPropFlags::SerializedByServo)) {
+    if (RefPtr<CSSValue> value = (this->*entry->mGetter)()) {
+      ErrorResult rv;
+      nsString text;
+      value->GetCssText(text, rv);
+      aReturn.Assign(text);
+      return rv.StealNSResult();
+    }
+    return NS_OK;
+  }
+
+  Servo_GetPropertyValue(mComputedStyle, prop, &aReturn);
   return NS_OK;
 }
 
 /* static */
 already_AddRefed<ComputedStyle>
 nsComputedDOMStyle::GetComputedStyle(Element* aElement,
                                      nsAtom* aPseudo,
                                      StyleType aStyleType)
@@ -982,67 +1009,16 @@ nsComputedDOMStyle::ClearCurrentStyleSou
     ClearComputedStyle();
   }
 
   mOuterFrame = nullptr;
   mInnerFrame = nullptr;
   mPresShell = nullptr;
 }
 
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::GetPropertyCSSValueWithoutWarning(
-  const nsAString& aPropertyName,
-  ErrorResult& aRv)
-{
-  nsCSSPropertyID prop =
-    nsCSSProps::LookupProperty(aPropertyName, CSSEnabledState::eForAllContent);
-
-  bool needsLayoutFlush;
-  ComputedStyleMap::Entry::ComputeMethod getter;
-
-  if (prop == eCSSPropertyExtra_variable) {
-    needsLayoutFlush = false;
-    getter = nullptr;
-  } else {
-    const ComputedStyleMap::Entry* propEntry =
-      GetComputedStyleMap()->FindEntryForProperty(prop);
-
-    if (!propEntry) {
-#ifdef DEBUG_ComputedDOMStyle
-      NS_WARNING(PromiseFlatCString(NS_ConvertUTF16toUTF8(aPropertyName) +
-                                    NS_LITERAL_CSTRING(" is not queryable!")).get());
-#endif
-
-      // NOTE:  For branches, we should flush here for compatibility!
-      return nullptr;
-    }
-
-    needsLayoutFlush = propEntry->IsLayoutFlushNeeded();
-    getter = propEntry->mGetter;
-  }
-
-  UpdateCurrentStyleSources(needsLayoutFlush);
-  if (!mComputedStyle) {
-    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return nullptr;
-  }
-
-  RefPtr<CSSValue> val;
-  if (prop == eCSSPropertyExtra_variable) {
-    val = DoGetCustomProperty(aPropertyName);
-  } else {
-    // Call our pointer-to-member-function.
-    val = (this->*getter)();
-  }
-
-  ClearCurrentStyleSources();
-
-  return val.forget();
-}
-
 NS_IMETHODIMP
 nsComputedDOMStyle::RemoveProperty(const nsAString& aPropertyName,
                                    nsAString& aReturn)
 {
   return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
 }
 
 
@@ -7187,36 +7163,16 @@ nsComputedDOMStyle::DoGetAnimationPlaySt
 }
 
 static void
 MarkComputedStyleMapDirty(const char* aPref, void* aData)
 {
   static_cast<ComputedStyleMap*>(aData)->MarkDirty();
 }
 
-already_AddRefed<CSSValue>
-nsComputedDOMStyle::DoGetCustomProperty(const nsAString& aPropertyName)
-{
-  MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
-
-  nsString variableValue;
-  const nsAString& name = Substring(aPropertyName,
-                                    CSS_CUSTOM_NAME_PREFIX_LENGTH);
-  bool present =
-    Servo_GetCustomPropertyValue(mComputedStyle, &name, &variableValue);
-  if (!present) {
-    return nullptr;
-  }
-
-  RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
-  val->SetString(variableValue);
-
-  return val.forget();
-}
-
 void
 nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent)
 {
   NS_ASSERTION(mContent == aContent, "didn't we register mContent?");
   NS_ASSERTION(mResolvedComputedStyle,
                "should have only registered an observer when "
                "mResolvedComputedStyle is true");
 
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -54,20 +54,16 @@ class nsComputedDOMStyle final : public 
                                , public nsStubMutationObserver
 {
 private:
   // Convenience typedefs:
   typedef nsCSSKTableEntry KTableEntry;
   typedef mozilla::dom::CSSValue CSSValue;
   typedef mozilla::StyleGeometryBox StyleGeometryBox;
 
-  already_AddRefed<CSSValue>
-  GetPropertyCSSValueWithoutWarning(const nsAString& aProp,
-                                    mozilla::ErrorResult& aRv);
-
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsComputedDOMStyle,
                                                                    nsICSSDeclaration)
 
   NS_DECL_NSIDOMCSSSTYLEDECLARATION_HELPER
   nsresult GetPropertyValue(const nsCSSPropertyID aPropID,
                             nsAString& aValue) override;
@@ -612,19 +608,16 @@ private:
 
   already_AddRefed<CSSValue> DoGetClipPath();
   already_AddRefed<CSSValue> DoGetFilter();
   already_AddRefed<CSSValue> DoGetMaskType();
   already_AddRefed<CSSValue> DoGetPaintOrder();
 
   already_AddRefed<CSSValue> DoGetContextProperties();
 
-  /* Custom properties */
-  already_AddRefed<CSSValue> DoGetCustomProperty(const nsAString& aPropertyName);
-
   /* Helper functions */
   void SetToRGBAColor(nsROCSSPrimitiveValue* aValue, nscolor aColor);
   void SetValueFromComplexColor(nsROCSSPrimitiveValue* aValue,
                                 const mozilla::StyleComplexColor& aColor);
   void SetValueForWidgetColor(nsROCSSPrimitiveValue* aValue,
                               const mozilla::StyleComplexColor& aColor,
                               uint8_t aWidgetType);
   void SetValueToStyleImage(const nsStyleImage& aStyleImage,
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -221,16 +221,20 @@ class Longhand(object):
             # discrete). For now, it is still non-animatable.
             self.animatable = False
             self.transitionable = False
             self.animation_value_type = None
 
         # See compute_damage for the various values this can take
         self.servo_restyle_damage = servo_restyle_damage
 
+    @staticmethod
+    def type():
+        return "longhand"
+
     def experimental(self, product):
         if product == "gecko":
             return bool(self.gecko_pref)
         return bool(self.servo_pref)
 
     # FIXME(emilio): Shorthand and Longhand should really share a base class.
     def explicitly_enabled_in_ua_sheets(self):
         return self.enabled_in in ["ua", "chrome"]
@@ -356,16 +360,20 @@ class Shorthand(object):
             if sub.transitionable:
                 transitionable = True
                 break
         return transitionable
 
     animatable = property(get_animatable)
     transitionable = property(get_transitionable)
 
+    @staticmethod
+    def type():
+        return "shorthand"
+
     def experimental(self, product):
         if product == "gecko":
             return bool(self.gecko_pref)
         return bool(self.servo_pref)
 
     # FIXME(emilio): Shorthand and Longhand should really share a base class.
     def explicitly_enabled_in_ua_sheets(self):
         return self.enabled_in in ["ua", "chrome"]
@@ -387,16 +395,20 @@ class Alias(object):
         self.camel_case = to_camel_case(self.ident)
         self.original = original
         self.enabled_in = original.enabled_in
         self.servo_pref = original.servo_pref
         self.gecko_pref = gecko_pref
         self.allowed_in_page_rule = original.allowed_in_page_rule
         self.allowed_in_keyframe_block = original.allowed_in_keyframe_block
 
+    @staticmethod
+    def type():
+        return "alias"
+
     def experimental(self, product):
         if product == "gecko":
             return bool(self.gecko_pref)
         return bool(self.servo_pref)
 
     def explicitly_enabled_in_ua_sheets(self):
         return self.enabled_in in ["ua", "chrome"]
 
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -17,18 +17,17 @@ use custom_properties::CustomPropertiesB
 use servo_arc::{Arc, UniqueArc};
 use smallbitvec::SmallBitVec;
 use std::borrow::Cow;
 use std::{ops, ptr};
 use std::cell::RefCell;
 use std::fmt::{self, Write};
 use std::mem::{self, ManuallyDrop};
 
-#[cfg(feature = "servo")] use cssparser::RGBA;
-use cssparser::{Parser, TokenSerializationType};
+use cssparser::{Parser, RGBA, TokenSerializationType};
 use cssparser::ParserInput;
 #[cfg(feature = "servo")] use euclid::SideOffsets2D;
 use context::QuirksMode;
 use font_metrics::FontMetricsProvider;
 #[cfg(feature = "gecko")] use gecko_bindings::bindings;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
 #[cfg(feature = "servo")] use logical_geometry::LogicalMargin;
 #[cfg(feature = "servo")] use computed_values;
@@ -40,17 +39,16 @@ use parser::ParserContext;
 use rule_cache::{RuleCache, RuleCacheConditions};
 use selector_parser::PseudoElement;
 use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")] use servo_config::prefs::PREFS;
 use shared_lock::StylesheetGuards;
 use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 use stylesheets::{CssRuleType, Origin, UrlExtraData};
-#[cfg(feature = "servo")] use values::Either;
 use values::generics::text::LineHeight;
 use values::computed;
 use values::computed::NonNegativeLength;
 use values::serialize_atom_name;
 use rule_tree::{CascadeLevel, StrongRuleNode};
 use self::computed_value_flags::*;
 use str::{CssString, CssStringBorrow, CssStringWriter};
 use style_adjuster::StyleAdjuster;
@@ -829,23 +827,23 @@ bitflags! {
         /// absolutely positioned elements.
         const ABSPOS_CB = 1 << 2;
         /// This longhand property applies to ::first-letter.
         const APPLIES_TO_FIRST_LETTER = 1 << 3;
         /// This longhand property applies to ::first-line.
         const APPLIES_TO_FIRST_LINE = 1 << 4;
         /// This longhand property applies to ::placeholder.
         const APPLIES_TO_PLACEHOLDER = 1 << 5;
+        /// This property's getComputedStyle implementation requires layout
+        /// to be flushed.
+        const GETCS_NEEDS_LAYOUT_FLUSH = 1 << 6;
 
         /* The following flags are currently not used in Rust code, they
          * only need to be listed in corresponding properties so that
          * they can be checked in the C++ side via ServoCSSPropList.h. */
-        /// This property's getComputedStyle implementation requires layout
-        /// to be flushed.
-        const GETCS_NEEDS_LAYOUT_FLUSH = 0;
         /// This property can be animated on the compositor.
         const CAN_ANIMATE_ON_COMPOSITOR = 0;
     }
 }
 
 /// An identifier for a given longhand property.
 #[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq)]
 #[repr(u16)]
@@ -2609,16 +2607,69 @@ impl ComputedValues {
     pub fn visited_rules(&self) -> Option<<&StrongRuleNode> {
         self.visited_style.as_ref().and_then(|s| s.rules.as_ref())
     }
 
     /// Gets a reference to the custom properties map (if one exists).
     pub fn custom_properties(&self) -> Option<<&Arc<::custom_properties::CustomPropertiesMap>> {
         self.custom_properties.as_ref()
     }
+
+    /// Writes the value of the given longhand as a string in `dest`.
+    ///
+    /// Note that the value will usually be the computed value, except for
+    /// colors, where it's resolved.
+    pub fn get_longhand_property_value<W>(
+        &self,
+        property_id: LonghandId,
+        dest: &mut CssWriter<W>
+    ) -> fmt::Result
+    where
+        W: Write,
+    {
+        // TODO(emilio): Is it worth to merge branches here just like
+        // PropertyDeclaration::to_css does?
+        //
+        // We'd need to get a concept of ~resolved value, which may not be worth
+        // it.
+        match property_id {
+            % for prop in data.longhands:
+            LonghandId::${prop.camel_case} => {
+                let style_struct =
+                    self.get_${prop.style_struct.ident.strip("_")}();
+                let value =
+                    style_struct
+                    % if prop.logical:
+                    .clone_${prop.ident}(self.writing_mode);
+                    % else:
+                    .clone_${prop.ident}();
+                    % endif
+
+                % if prop.predefined_type == "Color":
+                let value = self.resolve_color(value);
+                % endif
+
+                value.to_css(dest)
+            }
+            % endfor
+        }
+    }
+
+    /// Resolves the currentColor keyword.
+    ///
+    /// Any color value from computed values (except for the 'color' property
+    /// itself) should go through this method.
+    ///
+    /// Usage example:
+    /// let top_color =
+    ///   style.resolve_color(style.get_border().clone_border_top_color());
+    #[inline]
+    pub fn resolve_color(&self, color: computed::Color) -> RGBA {
+        color.to_rgba(self.get_color().clone_color())
+    }
 }
 
 #[cfg(feature = "servo")]
 impl ComputedValues {
     /// Create a new refcounted `ComputedValues`
     pub fn new(
         _: &Device,
         _: Option<<&ComputedValues>,
@@ -2721,28 +2772,16 @@ impl ComputedValuesInner {
     }
 
     /// Whether the current style is multicolumn.
     #[inline]
     pub fn is_multicol(&self) -> bool {
         self.get_column().is_multicol()
     }
 
-    /// Resolves the currentColor keyword.
-    ///
-    /// Any color value from computed values (except for the 'color' property
-    /// itself) should go through this method.
-    ///
-    /// Usage example:
-    /// let top_color = style.resolve_color(style.Border.border_top_color);
-    #[inline]
-    pub fn resolve_color(&self, color: computed::Color) -> RGBA {
-        color.to_rgba(self.get_color().color)
-    }
-
     /// Get the logical computed inline size.
     #[inline]
     pub fn content_inline_size(&self) -> computed::LengthOrPercentageOrAuto {
         let position_style = self.get_position();
         if self.writing_mode.is_vertical() {
             position_style.height
         } else {
             position_style.width
@@ -2897,29 +2936,29 @@ impl ComputedValuesInner {
 
         // Neither perspective nor transform present
         false
     }
 
     /// Serializes the computed value of this property as a string.
     pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String {
         match property {
-            % for style_struct in data.active_style_structs():
-                % for longhand in style_struct.longhands:
-                    PropertyDeclarationId::Longhand(LonghandId::${longhand.camel_case}) => {
-                        self.${style_struct.ident}.${longhand.ident}.to_css_string()
-                    }
-                % endfor
-            % endfor
+            PropertyDeclarationId::Longhand(id) => {
+                let mut s = String::new();
+                self.get_longhand_property_value(
+                    property,
+                    &mut CssWriter::new(&mut s)
+                ).unwrap();
+                s
+            }
             PropertyDeclarationId::Custom(name) => {
                 self.custom_properties
                     .as_ref()
                     .and_then(|map| map.get(name))
-                    .map(|value| value.to_css_string())
-                    .unwrap_or(String::new())
+                    .map_or(String::new(), |value| value.to_css_string())
             }
         }
     }
 }
 
 % if product == "gecko":
     pub use ::servo_arc::RawOffsetArc as BuilderArc;
     /// Clone an arc, returning a regular arc
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -169,19 +169,17 @@ use super::stylesheet_loader::{AsyncStyl
 trait ClosureHelper {
     fn invoke(&self, decls: &PropertyDeclarationBlock);
 }
 
 impl ClosureHelper for DeclarationBlockMutationClosure {
     #[inline]
     fn invoke(&self, decls: &PropertyDeclarationBlock) {
         if let Some(function) = self.function.as_ref() {
-            unsafe {
-                function(decls as *const _ as *const _, self.data);
-            }
+            unsafe { function(decls, self.data) };
         }
     }
 }
 
 /*
  * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
  * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
  * those signatures as well, giving us a second declaration of all the Servo_* functions in this
@@ -3418,18 +3416,17 @@ pub unsafe extern "C" fn Servo_Declarati
     })
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_UnlockedDeclarationBlock_GetCssText(
     declarations: *const structs::RawServoUnlockedDeclarationBlock,
     result: *mut nsAString,
 ) {
-    let decls = &*(declarations as *const PropertyDeclarationBlock);
-    decls.to_css(&mut *result).unwrap()
+    (*declarations).to_css(&mut *result).unwrap()
 }
 
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_SerializeOneValue(
     declarations: RawServoDeclarationBlockBorrowed,
     property_id: nsCSSPropertyID, buffer: *mut nsAString,
     computed_values: ComputedStyleBorrowedOrNull,
@@ -5131,16 +5128,35 @@ pub extern "C" fn Servo_StyleSet_HasDocu
 ) -> bool {
     let state = DocumentState::from_bits_truncate(state);
     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
 
     data.stylist.has_document_state_dependency(state)
 }
 
 #[no_mangle]
+pub unsafe extern "C" fn Servo_GetPropertyValue(
+    computed_values: ComputedStyleBorrowed,
+    prop: nsCSSPropertyID,
+    value: *mut nsAString,
+) {
+    use style::properties::PropertyFlags;
+
+    let longhand = LonghandId::from_nscsspropertyid(prop).expect("Not a longhand?");
+    debug_assert!(
+        !longhand.flags().contains(PropertyFlags::GETCS_NEEDS_LAYOUT_FLUSH),
+        "We're not supposed to serialize layout-dependent properties"
+    );
+    computed_values.get_longhand_property_value(
+        longhand,
+        &mut CssWriter::new(&mut *value),
+    ).unwrap();
+}
+
+#[no_mangle]
 pub unsafe extern "C" fn Servo_GetCustomPropertyValue(
     computed_values: ComputedStyleBorrowed,
     name: *const nsAString,
     value: *mut nsAString,
 ) -> bool {
     let custom_properties = match computed_values.custom_properties() {
         Some(p) => p,
         None => return false,