Bug 1296477 - stylo: Implement remaining `content` values; r?heycam draft
authorManish Goregaokar <manishearth@gmail.com>
Sat, 11 Mar 2017 22:40:54 -0800
changeset 497195 a210144ac458faca3a23b62a7914e5d533402010
parent 497194 8d9f2d5e16345adca387bd737d4610bdb71f8996
child 548823 74ffb6626b862edb9dd03fb308db7f9a245594f7
push id48825
push userbmo:manishearth@gmail.com
push dateSun, 12 Mar 2017 11:31:45 +0000
reviewersheycam
bugs1296477
milestone55.0a1
Bug 1296477 - stylo: Implement remaining `content` values; r?heycam MozReview-Commit-ID: 9fLM5bdR8hr
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
servo/components/style/build_gecko.rs
servo/components/style/gecko/conversions.rs
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/gecko_bindings/sugar/ns_css_value.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/counters.mako.rs
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -858,49 +858,40 @@ void
 Gecko_SetGradientImageValue(nsStyleImage* aImage, nsStyleGradient* aGradient)
 {
   MOZ_ASSERT(aImage);
   aImage->SetGradientData(aGradient);
 }
 
 static already_AddRefed<nsStyleImageRequest>
 CreateStyleImageRequest(nsStyleImageRequest::Mode aModeFlags,
-                        const uint8_t* aURLString, uint32_t aURLStringLength,
-                        ThreadSafeURIHolder* aBaseURI,
-                        ThreadSafeURIHolder* aReferrer,
-                        ThreadSafePrincipalHolder* aPrincipal)
+                        ServoBundledURI aURI)
 {
-  MOZ_ASSERT(aURLString);
-  MOZ_ASSERT(aBaseURI);
-  MOZ_ASSERT(aReferrer);
-  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aURI.mURLString);
+  MOZ_ASSERT(aURI.mBaseURI);
+  MOZ_ASSERT(aURI.mReferrer);
+  MOZ_ASSERT(aURI.mPrincipal);
 
   nsString url;
-  nsDependentCSubstring urlString(reinterpret_cast<const char*>(aURLString),
-                                  aURLStringLength);
+  nsDependentCSubstring urlString(reinterpret_cast<const char*>(aURI.mURLString),
+                                  aURI.mURLStringLength);
   AppendUTF8toUTF16(urlString, url);
   RefPtr<nsStringBuffer> urlBuffer = nsCSSValue::BufferFromString(url);
 
   RefPtr<nsStyleImageRequest> req =
-    new nsStyleImageRequest(aModeFlags, urlBuffer, do_AddRef(aBaseURI),
-                            do_AddRef(aReferrer), do_AddRef(aPrincipal));
+    new nsStyleImageRequest(aModeFlags, urlBuffer, do_AddRef(aURI.mBaseURI),
+                            do_AddRef(aURI.mReferrer), do_AddRef(aURI.mPrincipal));
   return req.forget();
 }
 
 void
-Gecko_SetUrlImageValue(nsStyleImage* aImage,
-                       const uint8_t* aURLString, uint32_t aURLStringLength,
-                       ThreadSafeURIHolder* aBaseURI,
-                       ThreadSafeURIHolder* aReferrer,
-                       ThreadSafePrincipalHolder* aPrincipal)
+Gecko_SetUrlImageValue(nsStyleImage* aImage, ServoBundledURI aURI)
 {
   RefPtr<nsStyleImageRequest> req =
-    CreateStyleImageRequest(nsStyleImageRequest::Mode::Track,
-                            aURLString, aURLStringLength,
-                            aBaseURI, aReferrer, aPrincipal);
+    CreateStyleImageRequest(nsStyleImageRequest::Mode::Track, aURI);
   aImage->SetImageRequest(req.forget());
 }
 
 void
 Gecko_CopyImageValueFrom(nsStyleImage* aImage, const nsStyleImage* aOther)
 {
   MOZ_ASSERT(aImage);
   MOZ_ASSERT(aOther);
@@ -911,35 +902,42 @@ Gecko_CopyImageValueFrom(nsStyleImage* a
 void
 Gecko_SetCursorArrayLength(nsStyleUserInterface* aStyleUI, size_t aLen)
 {
   aStyleUI->mCursorImages.Clear();
   aStyleUI->mCursorImages.SetLength(aLen);
 }
 
 void
-Gecko_SetCursorImage(nsCursorImage* aCursor,
-                     const uint8_t* aURLString, uint32_t aURLStringLength,
-                     ThreadSafeURIHolder* aBaseURI,
-                     ThreadSafeURIHolder* aReferrer,
-                     ThreadSafePrincipalHolder* aPrincipal)
+Gecko_SetCursorImage(nsCursorImage* aCursor, ServoBundledURI aURI)
 {
   aCursor->mImage =
-    CreateStyleImageRequest(nsStyleImageRequest::Mode::Discard,
-                            aURLString, aURLStringLength,
-                            aBaseURI, aReferrer, aPrincipal);
+    CreateStyleImageRequest(nsStyleImageRequest::Mode::Discard, aURI);
 }
 
 void
 Gecko_CopyCursorArrayFrom(nsStyleUserInterface* aDest,
                           const nsStyleUserInterface* aSrc)
 {
   aDest->mCursorImages = aSrc->mCursorImages;
 }
 
+void
+Gecko_SetContentDataImage(nsStyleContentData* aContent, ServoBundledURI aURI)
+{
+  aContent->SetImageRequest(CreateStyleImageRequest(nsStyleImageRequest::Track, aURI));
+}
+
+void
+Gecko_SetContentDataArray(nsStyleContentData* aContent, nsStyleContentType aType, uint32_t aLen)
+{
+  nsCSSValue::Array* arr = nsCSSValue::Array::Create(aLen);
+  aContent->SetCounters(aType, arr);
+}
+
 nsStyleGradient*
 Gecko_CreateGradient(uint8_t aShape,
                      uint8_t aSize,
                      bool aRepeating,
                      bool aLegacySyntax,
                      uint32_t aStopCount)
 {
   nsStyleGradient* result = new nsStyleGradient();
@@ -973,19 +971,17 @@ Gecko_SetListStyleImageNone(nsStyleList*
   aList->mListStyleImage = nullptr;
 }
 
 void
 Gecko_SetListStyleImage(nsStyleList* aList,
                         ServoBundledURI aURI)
 {
   aList->mListStyleImage =
-    CreateStyleImageRequest(nsStyleImageRequest::Mode(0),
-                            aURI.mURLString, aURI.mURLStringLength,
-                            aURI.mBaseURI, aURI.mReferrer, aURI.mPrincipal);
+    CreateStyleImageRequest(nsStyleImageRequest::Mode(0), aURI);
 }
 
 void
 Gecko_CopyListStyleImageFrom(nsStyleList* aList, const nsStyleList* aSource)
 {
   aList->mListStyleImage = aSource->mListStyleImage;
 }
 
@@ -1024,16 +1020,54 @@ Gecko_CopyStyleContentsFrom(nsStyleConte
   aContent->AllocateContents(count);
 
   for (uint32_t i = 0; i < count; ++i) {
     aContent->ContentAt(i) = aOther->ContentAt(i);
   }
 }
 
 void
+Gecko_ClearAndResizeCounterIncrements(nsStyleContent* aContent, uint32_t aHowMany)
+{
+  aContent->AllocateCounterIncrements(aHowMany);
+}
+
+void
+Gecko_CopyCounterIncrementsFrom(nsStyleContent* aContent, const nsStyleContent* aOther)
+{
+  uint32_t count = aOther->CounterIncrementCount();
+
+  aContent->AllocateCounterIncrements(count);
+
+  for (uint32_t i = 0; i < count; ++i) {
+    const nsStyleCounterData& data = aOther->CounterIncrementAt(i);
+    aContent->SetCounterIncrementAt(i, data.mCounter, data.mValue);
+  }
+}
+
+void
+Gecko_ClearAndResizeCounterResets(nsStyleContent* aContent, uint32_t aHowMany)
+{
+  aContent->AllocateCounterResets(aHowMany);
+}
+
+void
+Gecko_CopyCounterResetsFrom(nsStyleContent* aContent, const nsStyleContent* aOther)
+{
+  uint32_t count = aOther->CounterResetCount();
+
+  aContent->AllocateCounterResets(count);
+
+  for (uint32_t i = 0; i < count; ++i) {
+    const nsStyleCounterData& data = aOther->CounterResetAt(i);
+    aContent->SetCounterResetAt(i, data.mCounter, data.mValue);
+  }
+}
+
+void
 Gecko_EnsureImageLayersLength(nsStyleImageLayers* aLayers, size_t aLen,
                               nsStyleImageLayers::LayerType aLayerType)
 {
   size_t oldLength = aLayers->mLayers.Length();
 
   aLayers->mLayers.EnsureLengthAtLeast(aLen);
 
   for (size_t i = oldLength; i < aLen; ++i) {
@@ -1324,20 +1358,24 @@ Gecko_CSSValue_GetCalc(nsCSSValueBorrowe
 void
 Gecko_CSSValue_SetFunction(nsCSSValueBorrowedMut aCSSValue, int32_t aLen)
 {
   nsCSSValue::Array* arr = nsCSSValue::Array::Create(aLen);
   aCSSValue->SetArrayValue(arr, eCSSUnit_Function);
 }
 
 void
-Gecko_CSSValue_SetString(nsCSSValueBorrowedMut aCSSValue, const nsString aString)
+Gecko_CSSValue_SetString(nsCSSValueBorrowedMut aCSSValue, const uint8_t* aString, uint32_t aLength)
 {
   MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null);
-  aCSSValue->SetStringValue(aString, eCSSUnit_String);
+  nsString string;
+  nsDependentCSubstring slice(reinterpret_cast<const char*>(aString),
+                                  aLength);
+  AppendUTF8toUTF16(slice, string);
+  aCSSValue->SetStringValue(string, eCSSUnit_String);
 }
 
 void
 Gecko_CSSValue_SetArray(nsCSSValueBorrowedMut aCSSValue, int32_t aLength)
 {
   MOZ_ASSERT(aCSSValue->GetUnit() == eCSSUnit_Null);
   RefPtr<nsCSSValue::Array> array
     = nsCSSValue::Array::Create(aLength);
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -218,21 +218,17 @@ void Gecko_CopyFontFamilyFrom(nsFont* ds
 void Gecko_SetListStyleType(nsStyleList* style_struct, uint32_t type);
 void Gecko_CopyListStyleTypeFrom(nsStyleList* dst, const nsStyleList* src);
 
 // background-image style.
 // TODO: support element() and -moz-image()
 void Gecko_SetNullImageValue(nsStyleImage* image);
 void Gecko_SetGradientImageValue(nsStyleImage* image, nsStyleGradient* gradient);
 void Gecko_SetUrlImageValue(nsStyleImage* image,
-                            const uint8_t* url_bytes,
-                            uint32_t url_length,
-                            ThreadSafeURIHolder* base_uri,
-                            ThreadSafeURIHolder* referrer,
-                            ThreadSafePrincipalHolder* principal);
+                            ServoBundledURI uri);
 void Gecko_CopyImageValueFrom(nsStyleImage* image, const nsStyleImage* other);
 
 nsStyleGradient* Gecko_CreateGradient(uint8_t shape,
                                       uint8_t size,
                                       bool repeating,
                                       bool legacy_syntax,
                                       uint32_t stops);
 
@@ -240,23 +236,23 @@ nsStyleGradient* Gecko_CreateGradient(ui
 void Gecko_SetListStyleImageNone(nsStyleList* style_struct);
 void Gecko_SetListStyleImage(nsStyleList* style_struct,
                              ServoBundledURI uri);
 void Gecko_CopyListStyleImageFrom(nsStyleList* dest, const nsStyleList* src);
 
 // cursor style.
 void Gecko_SetCursorArrayLength(nsStyleUserInterface* ui, size_t len);
 void Gecko_SetCursorImage(nsCursorImage* cursor,
-                          const uint8_t* string_bytes, uint32_t string_length,
-                          ThreadSafeURIHolder* base_uri,
-                          ThreadSafeURIHolder* referrer,
-                          ThreadSafePrincipalHolder* principal);
+                          ServoBundledURI uri);
 void Gecko_CopyCursorArrayFrom(nsStyleUserInterface* dest,
                                const nsStyleUserInterface* src);
 
+void Gecko_SetContentDataImage(nsStyleContentData* contentData, ServoBundledURI uri);
+void Gecko_SetContentDataArray(nsStyleContentData* contentData, nsStyleContentType type, uint32_t len);
+
 // Dirtiness tracking.
 uint32_t Gecko_GetNodeFlags(RawGeckoNodeBorrowed node);
 void Gecko_SetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 void Gecko_SetOwnerDocumentNeedsStyleFlush(RawGeckoElementBorrowed element);
 
 // Incremental restyle.
 // Also, we might want a ComputedValues to ComputedValues API for animations?
@@ -282,17 +278,23 @@ void Gecko_EnsureTArrayCapacity(void* ar
 // otherwise. This is ensured with rust traits for the relevant structs.
 void Gecko_ClearPODTArray(void* array, size_t elem_size, size_t elem_align);
 
 // Clear the mContents field in nsStyleContent. This is needed to run the
 // destructors, otherwise we'd leak the images (though we still don't support
 // those), strings, and whatnot.
 void Gecko_ClearAndResizeStyleContents(nsStyleContent* content,
                                        uint32_t how_many);
+void Gecko_ClearAndResizeCounterIncrements(nsStyleContent* content,
+                                           uint32_t how_many);
+void Gecko_ClearAndResizeCounterResets(nsStyleContent* content,
+                                       uint32_t how_many);
 void Gecko_CopyStyleContentsFrom(nsStyleContent* content, const nsStyleContent* other);
+void Gecko_CopyCounterResetsFrom(nsStyleContent* content, const nsStyleContent* other);
+void Gecko_CopyCounterIncrementsFrom(nsStyleContent* content, const nsStyleContent* other);
 
 void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len,
                                    nsStyleImageLayers::LayerType layer_type);
 
 void Gecko_EnsureStyleAnimationArrayLength(void* array, size_t len);
 void Gecko_EnsureStyleTransitionArrayLength(void* array, size_t len);
 
 mozilla::Keyframe* Gecko_AnimationAppendKeyframe(RawGeckoKeyframeListBorrowedMut keyframes,
@@ -350,17 +352,17 @@ nsStyleCoord::CalcValue Gecko_CSSValue_G
 
 void Gecko_CSSValue_SetAbsoluteLength(nsCSSValueBorrowedMut css_value, nscoord len);
 void Gecko_CSSValue_SetNumber(nsCSSValueBorrowedMut css_value, float number);
 void Gecko_CSSValue_SetKeyword(nsCSSValueBorrowedMut css_value, nsCSSKeyword keyword);
 void Gecko_CSSValue_SetPercentage(nsCSSValueBorrowedMut css_value, float percent);
 void Gecko_CSSValue_SetAngle(nsCSSValueBorrowedMut css_value, float radians);
 void Gecko_CSSValue_SetCalc(nsCSSValueBorrowedMut css_value, nsStyleCoord::CalcValue calc);
 void Gecko_CSSValue_SetFunction(nsCSSValueBorrowedMut css_value, int32_t len);
-void Gecko_CSSValue_SetString(nsCSSValueBorrowedMut css_value, const nsString string);
+void Gecko_CSSValue_SetString(nsCSSValueBorrowedMut css_value, const uint8_t* aString, uint32_t aLength);
 void Gecko_CSSValue_SetArray(nsCSSValueBorrowedMut css_value, int32_t len);
 void Gecko_CSSValue_SetURL(nsCSSValueBorrowedMut css_value, ServoBundledURI uri);
 void Gecko_CSSValue_SetLocal(nsCSSValueBorrowedMut css_value, const nsString family);
 void Gecko_CSSValue_SetInteger(nsCSSValueBorrowedMut css_value, int32_t integer);
 void Gecko_CSSValue_Drop(nsCSSValueBorrowedMut css_value);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSValueSharedList, CSSValueSharedList);
 bool Gecko_PropertyId_IsPrefEnabled(nsCSSPropertyID id);
 
--- a/servo/components/style/build_gecko.rs
+++ b/servo/components/style/build_gecko.rs
@@ -566,16 +566,18 @@ mod bindings {
             "nsIAtom",
             "nsMediaFeature",
             "nsRestyleHint",
             "nsStyleBackground",
             "nsStyleBorder",
             "nsStyleColor",
             "nsStyleColumn",
             "nsStyleContent",
+            "nsStyleContentData",
+            "nsStyleContentType",
             "nsStyleContext",
             "nsStyleCoord",
             "nsStyleCoord_Calc",
             "nsStyleCoord_CalcValue",
             "nsStyleDisplay",
             "nsStyleEffects",
             "nsStyleFilter",
             "nsStyleFont",
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -101,36 +101,31 @@ impl From<nsStyleCoord_CalcValue> for Le
 impl nsStyleImage {
     /// Set a given Servo `Image` value into this `nsStyleImage`.
     pub fn set(&mut self, image: Image, with_url: bool, cacheable: &mut bool) {
         match image {
             Image::Gradient(gradient) => {
                 self.set_gradient(gradient)
             },
             Image::Url(ref url) if with_url => {
-                let (ptr, len) = match url.as_slice_components() {
-                    Ok(value) | Err(value) => value
-                };
-                let extra_data = url.extra_data();
-                unsafe {
-                    Gecko_SetUrlImageValue(self,
-                                           ptr,
-                                           len as u32,
-                                           extra_data.base.get(),
-                                           extra_data.referrer.get(),
-                                           extra_data.principal.get());
+                if let Some(ffi) = url.for_ffi() {
+                    unsafe {
+                        Gecko_SetUrlImageValue(self, ffi);
+                        // We unfortunately must make any url() value uncacheable, since
+                        // the applicable declarations cache is not per document, but
+                        // global, and the imgRequestProxy objects we store in the style
+                        // structs don't like to be tracked by more than one document.
+                        //
+                        // FIXME(emilio): With the scoped TLS thing this is no longer
+                        // true, remove this line in a follow-up!
+                        *cacheable = false;
+                    }
+                } else {
+                    error!("Skipping url without extra data")
                 }
-                // We unfortunately must make any url() value uncacheable, since
-                // the applicable declarations cache is not per document, but
-                // global, and the imgRequestProxy objects we store in the style
-                // structs don't like to be tracked by more than one document.
-                //
-                // FIXME(emilio): With the scoped TLS thing this is no longer
-                // true, remove this line in a follow-up!
-                *cacheable = false;
             },
             _ => (),
         }
     }
 
     fn set_gradient(&mut self, gradient: Gradient) {
         use cssparser::Color as CSSColor;
         use gecko_bindings::structs::{NS_STYLE_GRADIENT_SHAPE_CIRCULAR, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL};
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -51,16 +51,22 @@ use gecko_bindings::structs::nsStyleColo
 unsafe impl Send for nsStyleColor {}
 unsafe impl Sync for nsStyleColor {}
 use gecko_bindings::structs::nsStyleColumn;
 unsafe impl Send for nsStyleColumn {}
 unsafe impl Sync for nsStyleColumn {}
 use gecko_bindings::structs::nsStyleContent;
 unsafe impl Send for nsStyleContent {}
 unsafe impl Sync for nsStyleContent {}
+use gecko_bindings::structs::nsStyleContentData;
+unsafe impl Send for nsStyleContentData {}
+unsafe impl Sync for nsStyleContentData {}
+use gecko_bindings::structs::nsStyleContentType;
+unsafe impl Send for nsStyleContentType {}
+unsafe impl Sync for nsStyleContentType {}
 use gecko_bindings::structs::nsStyleContext;
 unsafe impl Send for nsStyleContext {}
 unsafe impl Sync for nsStyleContext {}
 use gecko_bindings::structs::nsStyleCoord;
 unsafe impl Send for nsStyleCoord {}
 unsafe impl Sync for nsStyleCoord {}
 use gecko_bindings::structs::nsStyleCoord_Calc;
 unsafe impl Send for nsStyleCoord_Calc {}
@@ -604,20 +610,17 @@ extern "C" {
     pub fn Gecko_SetNullImageValue(image: *mut nsStyleImage);
 }
 extern "C" {
     pub fn Gecko_SetGradientImageValue(image: *mut nsStyleImage,
                                        gradient: *mut nsStyleGradient);
 }
 extern "C" {
     pub fn Gecko_SetUrlImageValue(image: *mut nsStyleImage,
-                                  url_bytes: *const u8, url_length: u32,
-                                  base_uri: *mut ThreadSafeURIHolder,
-                                  referrer: *mut ThreadSafeURIHolder,
-                                  principal: *mut ThreadSafePrincipalHolder);
+                                  uri: ServoBundledURI);
 }
 extern "C" {
     pub fn Gecko_CopyImageValueFrom(image: *mut nsStyleImage,
                                     other: *const nsStyleImage);
 }
 extern "C" {
     pub fn Gecko_CreateGradient(shape: u8, size: u8, repeating: bool,
                                 legacy_syntax: bool, stops: u32)
@@ -635,26 +638,31 @@ extern "C" {
                                         src: *const nsStyleList);
 }
 extern "C" {
     pub fn Gecko_SetCursorArrayLength(ui: *mut nsStyleUserInterface,
                                       len: usize);
 }
 extern "C" {
     pub fn Gecko_SetCursorImage(cursor: *mut nsCursorImage,
-                                string_bytes: *const u8, string_length: u32,
-                                base_uri: *mut ThreadSafeURIHolder,
-                                referrer: *mut ThreadSafeURIHolder,
-                                principal: *mut ThreadSafePrincipalHolder);
+                                uri: ServoBundledURI);
 }
 extern "C" {
     pub fn Gecko_CopyCursorArrayFrom(dest: *mut nsStyleUserInterface,
                                      src: *const nsStyleUserInterface);
 }
 extern "C" {
+    pub fn Gecko_SetContentDataImage(contentData: *mut nsStyleContentData,
+                                     uri: ServoBundledURI);
+}
+extern "C" {
+    pub fn Gecko_SetContentDataArray(contentData: *mut nsStyleContentData,
+                                     type_: nsStyleContentType, len: u32);
+}
+extern "C" {
     pub fn Gecko_GetNodeFlags(node: RawGeckoNodeBorrowed) -> u32;
 }
 extern "C" {
     pub fn Gecko_SetNodeFlags(node: RawGeckoNodeBorrowed, flags: u32);
 }
 extern "C" {
     pub fn Gecko_UnsetNodeFlags(node: RawGeckoNodeBorrowed, flags: u32);
 }
@@ -683,20 +691,36 @@ extern "C" {
 extern "C" {
     pub fn Gecko_DropElementSnapshot(snapshot: ServoElementSnapshotOwned);
 }
 extern "C" {
     pub fn Gecko_ClearAndResizeStyleContents(content: *mut nsStyleContent,
                                              how_many: u32);
 }
 extern "C" {
+    pub fn Gecko_ClearAndResizeCounterIncrements(content: *mut nsStyleContent,
+                                                 how_many: u32);
+}
+extern "C" {
+    pub fn Gecko_ClearAndResizeCounterResets(content: *mut nsStyleContent,
+                                             how_many: u32);
+}
+extern "C" {
     pub fn Gecko_CopyStyleContentsFrom(content: *mut nsStyleContent,
                                        other: *const nsStyleContent);
 }
 extern "C" {
+    pub fn Gecko_CopyCounterResetsFrom(content: *mut nsStyleContent,
+                                       other: *const nsStyleContent);
+}
+extern "C" {
+    pub fn Gecko_CopyCounterIncrementsFrom(content: *mut nsStyleContent,
+                                           other: *const nsStyleContent);
+}
+extern "C" {
     pub fn Gecko_EnsureImageLayersLength(layers: *mut nsStyleImageLayers,
                                          len: usize,
                                          layer_type:
                                              nsStyleImageLayers_LayerType);
 }
 extern "C" {
     pub fn Gecko_EnsureStyleAnimationArrayLength(array:
                                                      *mut ::std::os::raw::c_void,
@@ -871,17 +895,17 @@ extern "C" {
                                   calc: nsStyleCoord_CalcValue);
 }
 extern "C" {
     pub fn Gecko_CSSValue_SetFunction(css_value: nsCSSValueBorrowedMut,
                                       len: i32);
 }
 extern "C" {
     pub fn Gecko_CSSValue_SetString(css_value: nsCSSValueBorrowedMut,
-                                    string: nsString);
+                                    aString: *const u8, aLength: u32);
 }
 extern "C" {
     pub fn Gecko_CSSValue_SetArray(css_value: nsCSSValueBorrowedMut,
                                    len: i32);
 }
 extern "C" {
     pub fn Gecko_CSSValue_SetURL(css_value: nsCSSValueBorrowedMut,
                                  uri: ServoBundledURI);
--- a/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -1,27 +1,22 @@
 /* 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/. */
 
 //! Little helpers for `nsCSSValue`.
 
 use app_units::Au;
-use gecko_bindings::bindings::Gecko_CSSValue_Drop;
-use gecko_bindings::bindings::Gecko_CSSValue_GetAbsoluteLength;
-use gecko_bindings::bindings::Gecko_CSSValue_GetCalc;
-use gecko_bindings::bindings::Gecko_CSSValue_GetPercentage;
-use gecko_bindings::bindings::Gecko_CSSValue_SetAbsoluteLength;
-use gecko_bindings::bindings::Gecko_CSSValue_SetCalc;
-use gecko_bindings::bindings::Gecko_CSSValue_SetPercentage;
+use gecko_bindings::bindings;
 use gecko_bindings::structs::{nsCSSValue, nsCSSUnit, nsCSSValue_Array, nscolor};
 use std::mem;
-use std::ops::Index;
+use std::ops::{Index, IndexMut};
 use std::slice;
 use values::computed::LengthOrPercentage;
+use values::specified::url::SpecifiedUrl;
 
 impl nsCSSValue {
     /// Create a CSSValue with null unit, useful to be used as a return value.
     #[inline]
     pub fn null() -> Self {
         unsafe { mem::zeroed() }
     }
 
@@ -72,68 +67,101 @@ impl nsCSSValue {
         debug_assert!(!array.is_null());
         &*array
     }
 
     /// Sets LengthOrPercentage value to this nsCSSValue.
     pub unsafe fn set_lop(&mut self, lop: LengthOrPercentage) {
         match lop {
             LengthOrPercentage::Length(au) => {
-                Gecko_CSSValue_SetAbsoluteLength(self, au.0)
+                bindings::Gecko_CSSValue_SetAbsoluteLength(self, au.0)
             }
             LengthOrPercentage::Percentage(pc) => {
-                Gecko_CSSValue_SetPercentage(self, pc)
+                bindings::Gecko_CSSValue_SetPercentage(self, pc)
             }
             LengthOrPercentage::Calc(calc) => {
-                Gecko_CSSValue_SetCalc(self, calc.into())
+                bindings::Gecko_CSSValue_SetCalc(self, calc.into())
             }
         }
     }
 
     /// Returns LengthOrPercentage value.
     pub unsafe fn get_lop(&self) -> LengthOrPercentage {
         match self.mUnit {
             nsCSSUnit::eCSSUnit_Pixel => {
-                LengthOrPercentage::Length(Au(Gecko_CSSValue_GetAbsoluteLength(self)))
+                LengthOrPercentage::Length(Au(bindings::Gecko_CSSValue_GetAbsoluteLength(self)))
             },
             nsCSSUnit::eCSSUnit_Percent => {
-                LengthOrPercentage::Percentage(Gecko_CSSValue_GetPercentage(self))
+                LengthOrPercentage::Percentage(bindings::Gecko_CSSValue_GetPercentage(self))
             },
             nsCSSUnit::eCSSUnit_Calc => {
-                LengthOrPercentage::Calc(Gecko_CSSValue_GetCalc(self).into())
+                LengthOrPercentage::Calc(bindings::Gecko_CSSValue_GetCalc(self).into())
             },
             x => panic!("The unit should not be {:?}", x),
         }
     }
+
+    /// Set to a string value
+    pub fn set_string(&mut self, s: &str) {
+        unsafe { bindings::Gecko_CSSValue_SetString(self, s.as_ptr(), s.len() as u32) }
+    }
+
+    /// Set to a string value
+    pub fn set_url(&mut self, url: &SpecifiedUrl) {
+        if let Some(ffi) = url.for_ffi() {
+            unsafe { bindings::Gecko_CSSValue_SetURL(self, ffi) }
+        } else {
+            error!("Skipping url without extra data")
+        }
+    }
+
+    /// Set to an array of given length
+    pub fn set_array(&mut self, len: i32) {
+        unsafe { bindings::Gecko_CSSValue_SetArray(self, len) }
+    }
 }
 
 impl Drop for nsCSSValue {
     fn drop(&mut self) {
-        unsafe { Gecko_CSSValue_Drop(self) };
+        unsafe { bindings::Gecko_CSSValue_Drop(self) };
     }
 }
 
 impl nsCSSValue_Array {
-    /// Return the length of this `nsCSSShadowArray`
+    /// Return the length of this `nsCSSValue::Array`
     #[inline]
     pub fn len(&self) -> usize {
         self.mCount
     }
 
     #[inline]
     fn buffer(&self) -> *const nsCSSValue {
         self.mArray.as_ptr()
     }
 
     /// Get the array as a slice of nsCSSValues.
     #[inline]
     pub fn as_slice(&self) -> &[nsCSSValue] {
         unsafe { slice::from_raw_parts(self.buffer(), self.len()) }
     }
+
+    /// Get the array as a mutable slice of nsCSSValues.
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [nsCSSValue] {
+        unsafe { slice::from_raw_parts_mut(self.buffer() as *mut _, self.len()) }
+    }
 }
 
 impl Index<usize> for nsCSSValue_Array {
     type Output = nsCSSValue;
     #[inline]
     fn index(&self, i: usize) -> &nsCSSValue {
         &self.as_slice()[i]
     }
 }
+
+impl IndexMut<usize> for nsCSSValue_Array {
+    #[inline]
+    fn index_mut(&mut self, i: usize) -> &mut nsCSSValue {
+        &mut self.as_mut_slice()[i]
+    }
+}
+
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2566,17 +2566,17 @@ fn static_assert() {
                         // TODO handle currentColor
                         // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
                         Color::CurrentColor => 0,
                     };
                 }
                 Url(ref url) => {
                     unsafe {
                         if let Some(ffi) = url.for_ffi() {
-                            bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, ffi);
+                            // bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, ffi);
                         }
                     }
                 }
             }
         }
     }
 
     pub fn copy_filter_from(&mut self, other: &Self) {
@@ -3140,26 +3140,22 @@ clip-path
             }
         } as u8;
 
         unsafe {
             Gecko_SetCursorArrayLength(&mut self.gecko, v.images.len());
         }
         for i in 0..v.images.len() {
             let image = &v.images[i];
-            let extra_data = image.url.extra_data();
-            let (ptr, len) = match image.url.as_slice_components() {
-                Ok(value) | Err(value) => value,
-            };
             unsafe {
-                Gecko_SetCursorImage(&mut self.gecko.mCursorImages[i],
-                                     ptr, len as u32,
-                                     extra_data.base.get(),
-                                     extra_data.referrer.get(),
-                                     extra_data.principal.get());
+                if let Some(ffi) = image.url.for_ffi() {
+                    Gecko_SetCursorImage(&mut self.gecko.mCursorImages[i], ffi);
+                } else {
+                    error!("Skipping url without extra data")
+                }
             }
             // We don't need to record this struct as uncacheable, like when setting
             // background-image to a url() value, since only properties in reset structs
             // are re-used from the applicable declaration cache, and the Pointing struct
             // is an inherited struct.
         }
     }
 
@@ -3188,22 +3184,24 @@ clip-path
 
     ${impl_simple_copy('column_count', 'mColumnCount')}
 
     <% impl_app_units("column_rule_width", "mColumnRuleWidth", need_clone=True,
                       round_to_pixels=True) %>
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Counters"
-                  skip_longhands="content">
+                  skip_longhands="content counter-increment counter-reset">
     pub fn set_content(&mut self, v: longhands::content::computed_value::T) {
         use properties::longhands::content::computed_value::T;
         use properties::longhands::content::computed_value::ContentItem;
+        use style_traits::ToCss;
         use gecko_bindings::structs::nsStyleContentType::*;
         use gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents;
+        use gecko_bindings::bindings::Gecko_SetContentDataImage;
 
         // Converts a string as utf16, and returns an owned, zero-terminated raw buffer.
         fn as_utf16_and_forget(s: &str) -> *mut u16 {
             use std::mem;
             let mut vec = s.encode_utf16().collect::<Vec<_>>();
             vec.push(0u16);
             let ptr = vec.as_mut_ptr();
             mem::forget(vec);
@@ -3221,57 +3219,104 @@ clip-path
                 }
             },
             T::Content(items) => {
                 unsafe {
                     Gecko_ClearAndResizeStyleContents(&mut self.gecko,
                                                       items.len() as u32);
                 }
                 for (i, item) in items.into_iter().enumerate() {
-                    // TODO: Servo lacks support for attr(), and URIs.
                     // NB: Gecko compares the mString value if type is not image
                     // or URI independently of whatever gets there. In the quote
                     // cases, they set it to null, so do the same here.
                     unsafe {
                         *self.gecko.mContents[i].mContent.mString.as_mut() = ptr::null_mut();
                     }
                     match item {
                         ContentItem::String(value) => {
                             self.gecko.mContents[i].mType = eStyleContentType_String;
                             unsafe {
                                 // NB: we share allocators, so doing this is fine.
                                 *self.gecko.mContents[i].mContent.mString.as_mut() =
                                     as_utf16_and_forget(&value);
                             }
                         }
+                        ContentItem::Attr(value) => {
+                            self.gecko.mContents[i].mType = eStyleContentType_Attr;
+                            unsafe {
+                                // NB: we share allocators, so doing this is fine.
+                                *self.gecko.mContents[i].mContent.mString.as_mut() =
+                                    as_utf16_and_forget(&value);
+                            }
+                        }
                         ContentItem::OpenQuote
                             => self.gecko.mContents[i].mType = eStyleContentType_OpenQuote,
                         ContentItem::CloseQuote
                             => self.gecko.mContents[i].mType = eStyleContentType_CloseQuote,
                         ContentItem::NoOpenQuote
                             => self.gecko.mContents[i].mType = eStyleContentType_NoOpenQuote,
                         ContentItem::NoCloseQuote
                             => self.gecko.mContents[i].mType = eStyleContentType_NoCloseQuote,
                         ContentItem::MozAltContent
                             => self.gecko.mContents[i].mType = eStyleContentType_AltContent,
-                        ContentItem::Counter(..) |
-                        ContentItem::Counters(..)
-                            => self.gecko.mContents[i].mType = eStyleContentType_Uninitialized,
+                        ContentItem::Counter(name, style) => { 
+                            unsafe {
+                                bindings::Gecko_SetContentDataArray(&mut self.gecko.mContents[i], eStyleContentType_Counter, 2)
+                            }
+                            let mut array = unsafe { &mut **self.gecko.mContents[i].mContent.mCounters.as_mut() };
+                            array[0].set_string(&name);
+                            array[1].set_string(&style.to_css_string());
+                        }
+                        ContentItem::Counters(name, sep, style) => {
+                            unsafe {
+                                bindings::Gecko_SetContentDataArray(&mut self.gecko.mContents[i], eStyleContentType_Counters, 3)
+                            }
+                            let mut array = unsafe { &mut **self.gecko.mContents[i].mContent.mCounters.as_mut() };
+                            array[0].set_string(&name);
+                            array[1].set_string(&sep);
+                            array[2].set_string(&style.to_css_string());
+                        }
+                        ContentItem::Url(ref url) => {
+                            if let Some(ffi) = url.for_ffi() {
+                                unsafe { Gecko_SetContentDataImage(&mut self.gecko.mContents[i], ffi) }
+                            } else {
+                                error!("Skipping url without extra data")
+                            }
+                        }
                     }
                 }
             }
         }
     }
 
     pub fn copy_content_from(&mut self, other: &Self) {
         use gecko_bindings::bindings::Gecko_CopyStyleContentsFrom;
         unsafe {
             Gecko_CopyStyleContentsFrom(&mut self.gecko, &other.gecko)
         }
     }
+
+    % for counter_property in ["Increment", "Reset"]:
+        pub fn set_counter_${counter_property.lower()}(&mut self, v: longhands::counter_increment::computed_value::T) {
+            unsafe {
+                bindings::Gecko_ClearAndResizeCounter${counter_property}s(&mut self.gecko,
+                                                                      v.0.len() as u32);
+                for (i, item) in v.0.into_iter().enumerate() {
+                    self.gecko.m${counter_property}s[i].mCounter.assign_utf8(&item.0);
+                    self.gecko.m${counter_property}s[i].mValue = item.1;
+                }
+            }
+        }
+
+        pub fn copy_counter_${counter_property.lower()}_from(&mut self, other: &Self) {
+            unsafe {
+                bindings::Gecko_CopyCounter${counter_property}sFrom(&mut self.gecko, &other.gecko)
+            }
+        }
+    % endfor
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="XUL"
                   skip_longhands="-moz-stack-sizing">
 
     #[allow(non_snake_case)]
     pub fn set__moz_stack_sizing(&mut self, v: longhands::_moz_stack_sizing::computed_value::T) {
         use properties::longhands::_moz_stack_sizing::computed_value::T;
--- a/servo/components/style/properties/longhand/counters.mako.rs
+++ b/servo/components/style/properties/longhand/counters.mako.rs
@@ -5,32 +5,34 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
 
 <%helpers:longhand name="content" animatable="False" spec="https://drafts.csswg.org/css-content/#propdef-content">
     use cssparser::Token;
     use std::ascii::AsciiExt;
     use values::computed::ComputedValueAsSpecified;
+    use values::specified::url::SpecifiedUrl;
     use values::HasViewportPercentage;
 
     use super::list_style_type;
 
     pub use self::computed_value::T as SpecifiedValue;
     pub use self::computed_value::ContentItem;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use super::super::list_style_type;
 
         use cssparser;
         use std::fmt;
         use style_traits::ToCss;
+        use values::specified::url::SpecifiedUrl;
 
         #[derive(Debug, PartialEq, Eq, Clone)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub enum ContentItem {
             /// Literal string content.
             String(String),
             /// `counter(name, style)`.
             Counter(String, list_style_type::computed_value::T),
@@ -43,16 +45,20 @@
             /// `no-open-quote`.
             NoOpenQuote,
             /// `no-close-quote`.
             NoCloseQuote,
 
             % if product == "gecko":
                 /// `-moz-alt-content`
                 MozAltContent,
+                /// `attr(attr)`
+                Attr(String),
+                /// `url(url)`
+                Url(SpecifiedUrl),
             % endif
         }
 
         impl ToCss for ContentItem {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                 match *self {
                     ContentItem::String(ref s) => {
                         cssparser::serialize_string(&**s, dest)
@@ -75,16 +81,18 @@
                     }
                     ContentItem::OpenQuote => dest.write_str("open-quote"),
                     ContentItem::CloseQuote => dest.write_str("close-quote"),
                     ContentItem::NoOpenQuote => dest.write_str("no-open-quote"),
                     ContentItem::NoCloseQuote => dest.write_str("no-close-quote"),
 
                     % if product == "gecko":
                         ContentItem::MozAltContent => dest.write_str("-moz-alt-content"),
+                        ContentItem::Attr(ref attr) => write!(dest, "attr({})", attr),
+                        ContentItem::Url(ref url) => url.to_css(dest),
                     % endif
                 }
             }
         }
 
         #[allow(non_camel_case_types)]
         #[derive(Debug, PartialEq, Eq, Clone)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -130,16 +138,20 @@
         if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
             return Ok(SpecifiedValue::normal)
         }
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue::none)
         }
         let mut content = vec![];
         loop {
+            if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
+                content.push(ContentItem::Url(url));
+                continue;
+            }
             match input.next() {
                 Ok(Token::QuotedString(value)) => {
                     content.push(ContentItem::String(value.into_owned()))
                 }
                 Ok(Token::Function(name)) => {
                     content.push(try!(match_ignore_ascii_case! { &name,
                         "counter" => input.parse_nested_block(|input| {
                             let name = try!(input.expect_ident()).into_owned();
@@ -154,16 +166,19 @@
                             try!(input.expect_comma());
                             let separator = try!(input.expect_string()).into_owned();
                             let style = input.try(|input| {
                                 try!(input.expect_comma());
                                 list_style_type::parse(context, input)
                             }).unwrap_or(list_style_type::computed_value::T::decimal);
                             Ok(ContentItem::Counters(name, separator, style))
                         }),
+                        "attr" => input.parse_nested_block(|input| {
+                            Ok(ContentItem::Attr(input.expect_string()?.into_owned()))
+                        }),
                         _ => return Err(())
                     }));
                 }
                 Ok(Token::Ident(ident)) => {
                     match_ignore_ascii_case! { &ident,
                         "open-quote" => content.push(ContentItem::OpenQuote),
                         "close-quote" => content.push(ContentItem::CloseQuote),
                         "no-open-quote" => content.push(ContentItem::NoOpenQuote),
@@ -183,17 +198,17 @@
         if !content.is_empty() {
             Ok(SpecifiedValue::Content(content))
         } else {
             Err(())
         }
     }
 </%helpers:longhand>
 
-<%helpers:longhand name="counter-increment" products="servo" animatable="False"
+<%helpers:longhand name="counter-increment" animatable="False"
                    spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment">
     use std::fmt;
     use style_traits::ToCss;
     use super::content;
     use values::HasViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
 
     use cssparser::{Token, serialize_identifier};
@@ -257,17 +272,17 @@
         if !counters.is_empty() {
             Ok(SpecifiedValue(counters))
         } else {
             Err(())
         }
     }
 </%helpers:longhand>
 
-<%helpers:longhand name="counter-reset" products="servo" animatable="False"
+<%helpers:longhand name="counter-reset" animatable="False"
                    spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset">
     pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
     use super::counter_increment::{parse_common};
 
     pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         parse_common(0, input)
     }
 </%helpers:longhand>