Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Sat, 09 Jun 2018 00:53:46 +0300
changeset 422001 ac8a73f9dbb79f890176cdaaf4b044f51de364a2
parent 422000 54c9df81e0087d640cb822506f5162be96b6cafd (current diff)
parent 421940 4e1cee0e0a48d3e85bd3c20342354c95c5039d77 (diff)
child 422002 bef87c59a2c3a6d6897b8eb83b051858d9b9195e
child 422024 35bd1a865ff5262e05f2e421d499e8c42a5df2de
child 422056 437d495c93b657420be63d179b420f956050d4c9
push id104166
push usercbrindusan@mozilla.com
push dateFri, 08 Jun 2018 22:03:53 +0000
treeherdermozilla-inbound@bef87c59a2c3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
ac8a73f9dbb7 / 62.0a1 / 20180608220136 / files
nightly linux64
ac8a73f9dbb7 / 62.0a1 / 20180608220136 / files
nightly mac
ac8a73f9dbb7 / 62.0a1 / 20180608220136 / files
nightly win32
ac8a73f9dbb7 / 62.0a1 / 20180608220136 / files
nightly win64
ac8a73f9dbb7 / 62.0a1 / 20180608220136 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- 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,