Bug 783162: Make mapped attributes hold the image alive. r=bz
authorKyle Huey <khuey@kylehuey.com>
Fri, 24 Aug 2012 10:50:49 -0700
changeset 105358 8bd8ec63a020a56aa9d654d99c83bf6b97c5efe1
parent 105357 814de647442cdcc0cfc815fc06335750fe13a61b
child 105359 0eb750ce354b9f9b55e768012a6b809547429046
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersbz
bugs783162, 697230, 273078
milestone17.0a1
Bug 783162: Make mapped attributes hold the image alive. r=bz The nsCSSValue in nsGenericHTMLElement::MapBackgroundInto is a temporary. This causes a problem after Bug 697230 landed, because the nsCSSValue::Image we put into that value is destroyed once we're done doing style stuff. Previously the nsImageLoader would grab the request off the nsCSSValue::Image and hold it alive. Bug 697230 changed the behavior here; now when the nsCSSValue::Image is destroyed it tells the image loader to drop the request. The result is that all the references to the request are dropped and the frame is never told it has a background. The solution is to keep the nsCSSValue::Image alive longer. This patch adds two new types of nsAttrValue. The first is an nsCSSValue::URL. A ParseBackgroundAttribute method is added on nsGenericHTMLElement that the relevant elements (body/td/th/table/tr/tbody/thead/tfoot) call that parses background into an nsCSSValue::URL. The second is an nsCSSValue::Image. nsGenericHTMLElement::MapBackgroundInto attempts to convert the nsCSSValue::URL into an nsCSSValue::Image by kicking off the image load. The result is that image loads are only started when the element is actually visible. This also mirrors the way background-image works. This also allows us to fix two longstanding bugs in this code. Since MapBackgroundInto doesn't have a pointer to the actual element, it relied on grabbing the principal of the document. Now we can grab the principal of the node in ParseBackgroundAttribute. MapBackgroundInto also has no way to get at the element's base URI (to honor xml:base), which is now possible in ParseBackgroundAttribute. nsCSSValue::[Image|URL] have also been moved to be mozilla::css::[Image|URL]Value. nsAttrValue.h is included in external linkage code, so it can't include nsCSSValue.h to get the declarations of nsCSSValue::[Image|URL], and nested classes can't be forward declared. Moving the classes to a namespace solves the problem. Finally some old inoperative quirks mode code was removed. This code has done nothing since Bug 273078 was landed in 2004.
content/base/src/nsAttrValue.cpp
content/base/src/nsAttrValue.h
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLBodyElement.cpp
content/html/content/src/nsHTMLTableCellElement.cpp
content/html/content/src/nsHTMLTableElement.cpp
content/html/content/src/nsHTMLTableRowElement.cpp
content/html/content/src/nsHTMLTableSectionElement.cpp
dom/base/nsDOMClassInfo.cpp
layout/reftests/backgrounds/body-background-ref.html
layout/reftests/backgrounds/body-background.html
layout/reftests/backgrounds/div-background-ref.html
layout/reftests/backgrounds/div-background.html
layout/reftests/backgrounds/reftest.list
layout/reftests/backgrounds/table-background-ref.html
layout/reftests/backgrounds/table-background.html
layout/style/ImageLoader.cpp
layout/style/ImageLoader.h
layout/style/nsCSSParser.cpp
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleAnimation.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
--- a/content/base/src/nsAttrValue.cpp
+++ b/content/base/src/nsAttrValue.cpp
@@ -205,16 +205,26 @@ nsAttrValue::SetTo(const nsAttrValue& aO
       cont->mColor = otherCont->mColor;
       break;
     }
     case eCSSStyleRule:
     {
       NS_ADDREF(cont->mCSSStyleRule = otherCont->mCSSStyleRule);
       break;
     }
+    case eURL:
+    {
+      NS_ADDREF(cont->mURL = otherCont->mURL);
+      break;
+    }
+    case eImage:
+    {
+      NS_ADDREF(cont->mImage = otherCont->mImage);
+      break;
+    }
     case eAtomArray:
     {
       if (!EnsureEmptyAtomArray() ||
           !GetAtomArrayValue()->AppendElements(*otherCont->mAtomArray)) {
         Reset();
         return;
       }
       break;
@@ -310,16 +320,27 @@ nsAttrValue::SetTo(css::StyleRule* aValu
     MiscContainer* cont = GetMiscContainer();
     NS_ADDREF(cont->mCSSStyleRule = aValue);
     cont->mType = eCSSStyleRule;
     SetMiscAtomOrString(aSerialized);
   }
 }
 
 void
+nsAttrValue::SetTo(css::URLValue* aValue, const nsAString* aSerialized)
+{
+  if (EnsureEmptyMiscContainer()) {
+    MiscContainer* cont = GetMiscContainer();
+    NS_ADDREF(cont->mURL = aValue);
+    cont->mType = eURL;
+    SetMiscAtomOrString(aSerialized);
+  }
+}
+
+void
 nsAttrValue::SetTo(const nsIntMargin& aValue)
 {
   if (EnsureEmptyMiscContainer()) {
     MiscContainer* cont = GetMiscContainer();
     cont->mIntMargin = new nsIntMargin(aValue);
     cont->mType = eIntMarginValue;
   }
 }
@@ -769,16 +790,25 @@ nsAttrValue::HashValue() const
     case eColor:
     {
       return cont->mColor;
     }
     case eCSSStyleRule:
     {
       return NS_PTR_TO_INT32(cont->mCSSStyleRule);
     }
+    // Intentionally identical, so that loading the image does not change the
+    // hash code.
+    case eURL:
+    case eImage:
+    {
+      nsString str;
+      ToString(str);
+      return HashString(str);
+    }
     case eAtomArray:
     {
       uint32_t hash = 0;
       uint32_t count = cont->mAtomArray->Length();
       for (nsCOMPtr<nsIAtom> *cur = cont->mAtomArray->Elements(),
                              *end = cur + count;
            cur != end; ++cur) {
         hash = AddToHash(hash, cur->get());
@@ -865,16 +895,24 @@ nsAttrValue::Equals(const nsAttrValue& a
         needsStringComparison = true;
       }
       break;
     }
     case eCSSStyleRule:
     {
       return thisCont->mCSSStyleRule == otherCont->mCSSStyleRule;
     }
+    case eURL:
+    {
+      return thisCont->mURL == otherCont->mURL;
+    }
+    case eImage:
+    {
+      return thisCont->mImage == otherCont->mImage;
+    }
     case eAtomArray:
     {
       // For classlists we could be insensitive to order, however
       // classlists are never mapped attributes so they are never compared.
 
       if (!(*thisCont->mAtomArray == *otherCont->mAtomArray)) {
         return false;
       }
@@ -1495,16 +1533,41 @@ nsAttrValue::ParseIntMarginValue(const n
     cont->mType = eIntMarginValue;
     SetMiscAtomOrString(&aString);
     return true;
   }
 
   return false;
 }
 
+bool
+nsAttrValue::LoadImage(nsIDocument* aDocument)
+{
+  NS_ASSERTION(Type() == eURL, "wrong type");
+
+  nsString val;
+  ToString(val);
+  if (val.IsEmpty()) {
+    return false;
+  }
+
+  MiscContainer* cont = GetMiscContainer();
+  mozilla::css::URLValue* url = cont->mURL;
+  mozilla::css::ImageValue* image = 
+    new css::ImageValue(url->GetURI(), url->mString, url->mReferrer,
+                        url->mOriginPrincipal, aDocument);
+
+  NS_ADDREF(image);
+  cont->mImage = image;
+  NS_RELEASE(url);
+  cont->mType = eImage;
+
+  return true;
+}
+
 void
 nsAttrValue::SetMiscAtomOrString(const nsAString* aValue)
 {
   NS_ASSERTION(GetMiscContainer(), "Must have MiscContainer!");
   NS_ASSERTION(!GetMiscContainer()->mStringBits,
                "Trying to re-set atom or string!");
   if (aValue) {
     uint32_t len = aValue->Length();
@@ -1571,16 +1634,26 @@ nsAttrValue::EnsureEmptyMiscContainer()
     ResetMiscAtomOrString();
     cont = GetMiscContainer();
     switch (cont->mType) {
       case eCSSStyleRule:
       {
         NS_RELEASE(cont->mCSSStyleRule);
         break;
       }
+      case eURL:
+      {
+        NS_RELEASE(cont->mURL);
+        break;
+      }
+      case eImage:
+      {
+        NS_RELEASE(cont->mImage);
+        break;
+      }
       case eAtomArray:
       {
         delete cont->mAtomArray;
         break;
       }
       case eIntMarginValue:
       {
         delete cont->mIntMargin;
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -18,22 +18,25 @@
 #include "nsCaseTreatment.h"
 #include "nsMargin.h"
 #include "nsCOMPtr.h"
 #include "SVGAttrValueWrapper.h"
 
 typedef PRUptrdiff PtrBits;
 class nsAString;
 class nsIAtom;
+class nsIDocument;
 template<class E, class A> class nsTArray;
 struct nsTArrayDefaultAllocator;
 
 namespace mozilla {
 namespace css {
 class StyleRule;
+struct URLValue;
+struct ImageValue;
 }
 }
 
 #define NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM 12
 
 #define NS_ATTRVALUE_BASETYPE_MASK (PtrBits(3))
 #define NS_ATTRVALUE_POINTERVALUE_MASK (~NS_ATTRVALUE_BASETYPE_MASK)
 
@@ -86,46 +89,49 @@ public:
     eAtom =         0x02, //   10
     eInteger =      0x03, // 0011
     eColor =        0x07, // 0111
     eEnum =         0x0B, // 1011  This should eventually die
     ePercent =      0x0F, // 1111
     // Values below here won't matter, they'll be always stored in the 'misc'
     // struct.
     eCSSStyleRule =    0x10
-    ,eAtomArray =      0x11
-    ,eDoubleValue  =   0x12
-    ,eIntMarginValue = 0x13
-    ,eSVGTypesBegin =  0x14
+    ,eURL =            0x11
+    ,eImage =          0x12
+    ,eAtomArray =      0x13
+    ,eDoubleValue  =   0x14
+    ,eIntMarginValue = 0x15
+    ,eSVGTypesBegin =  0x16
     ,eSVGAngle =       eSVGTypesBegin
-    ,eSVGIntegerPair = 0x15
-    ,eSVGLength =      0x16
-    ,eSVGLengthList =  0x17
-    ,eSVGNumberList =  0x18
-    ,eSVGNumberPair =  0x19
-    ,eSVGPathData   =  0x20
-    ,eSVGPointList  =  0x21
-    ,eSVGPreserveAspectRatio = 0x22
-    ,eSVGStringList =  0x23
-    ,eSVGTransformList = 0x24
-    ,eSVGViewBox =     0x25
+    ,eSVGIntegerPair = 0x17
+    ,eSVGLength =      0x18
+    ,eSVGLengthList =  0x19
+    ,eSVGNumberList =  0x20
+    ,eSVGNumberPair =  0x21
+    ,eSVGPathData   =  0x22
+    ,eSVGPointList  =  0x23
+    ,eSVGPreserveAspectRatio = 0x24
+    ,eSVGStringList =  0x25
+    ,eSVGTransformList = 0x26
+    ,eSVGViewBox =     0x27
     ,eSVGTypesEnd =    0x34
   };
 
   ValueType Type() const;
 
   void Reset();
 
   void SetTo(const nsAttrValue& aOther);
   void SetTo(const nsAString& aValue);
   void SetTo(nsIAtom* aValue);
   void SetTo(int16_t aInt);
   void SetTo(int32_t aInt, const nsAString* aSerialized);
   void SetTo(double aValue, const nsAString* aSerialized);
   void SetTo(mozilla::css::StyleRule* aValue, const nsAString* aSerialized);
+  void SetTo(mozilla::css::URLValue* aValue, const nsAString* aSerialized);
   void SetTo(const nsIntMargin& aValue);
   void SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized);
   void SetTo(const nsSVGIntegerPair& aValue, const nsAString* aSerialized);
   void SetTo(const nsSVGLength2& aValue, const nsAString* aSerialized);
   void SetTo(const mozilla::SVGLengthList& aValue,
              const nsAString* aSerialized);
   void SetTo(const mozilla::SVGNumberList& aValue,
              const nsAString* aSerialized);
@@ -164,16 +170,18 @@ public:
   const nsCheapString GetStringValue() const;
   inline nsIAtom* GetAtomValue() const;
   inline int32_t GetIntegerValue() const;
   bool GetColorValue(nscolor& aColor) const;
   inline int16_t GetEnumValue() const;
   inline float GetPercentValue() const;
   inline AtomArray* GetAtomArrayValue() const;
   inline mozilla::css::StyleRule* GetCSSStyleRuleValue() const;
+  inline mozilla::css::URLValue* GetURLValue() const;
+  inline mozilla::css::ImageValue* GetImageValue() const;
   inline double GetDoubleValue() const;
   bool GetIntMarginValue(nsIntMargin& aMargin) const;
 
   /**
    * Returns the string corresponding to the stored enum value.
    *
    * @param aResult   the string representing the enum tag
    * @param aRealTag  wheter we want to have the real tag or the saved one
@@ -337,16 +345,24 @@ public:
    * Parse a margin string of format 'top, right, bottom, left' into
    * an nsIntMargin.
    *
    * @param aString the string to parse
    * @return whether the value could be parsed
    */
   bool ParseIntMarginValue(const nsAString& aString);
 
+  /**
+   * Convert a URL nsAttrValue to an Image nsAttrValue.
+   *
+   * @param aDocument the document this nsAttrValue belongs to.
+   * @return whether an image load was attempted
+   */
+  bool LoadImage(nsIDocument* aDocument);
+
   size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
 
 private:
   // These have to be the same as in ValueType
   enum ValueBaseType {
     eStringBase =    eString,    // 00
     eOtherBase =     0x01,       // 01
     eAtomBase =      eAtom,      // 10
@@ -362,16 +378,18 @@ private:
     // mStringBits.
     PtrBits mStringBits;
     union {
       int32_t mInteger;
       nscolor mColor;
       uint32_t mEnumValue;
       int32_t mPercent;
       mozilla::css::StyleRule* mCSSStyleRule;
+      mozilla::css::URLValue* mURL;
+      mozilla::css::ImageValue* mImage;
       AtomArray* mAtomArray;
       double mDoubleValue;
       nsIntMargin* mIntMargin;
       const nsSVGAngle* mSVGAngle;
       const nsSVGIntegerPair* mSVGIntegerPair;
       const nsSVGLength2* mSVGLength;
       const mozilla::SVGLengthList* mSVGLengthList;
       const mozilla::SVGNumberList* mSVGNumberList;
@@ -490,16 +508,30 @@ nsAttrValue::GetAtomArrayValue() const
 
 inline mozilla::css::StyleRule*
 nsAttrValue::GetCSSStyleRuleValue() const
 {
   NS_PRECONDITION(Type() == eCSSStyleRule, "wrong type");
   return GetMiscContainer()->mCSSStyleRule;
 }
 
+inline mozilla::css::URLValue*
+nsAttrValue::GetURLValue() const
+{
+  NS_PRECONDITION(Type() == eURL, "wrong type");
+  return GetMiscContainer()->mURL;
+}
+
+inline mozilla::css::ImageValue*
+nsAttrValue::GetImageValue() const
+{
+  NS_PRECONDITION(Type() == eImage, "wrong type");
+  return GetMiscContainer()->mImage;
+}
+
 inline double
 nsAttrValue::GetDoubleValue() const
 {
   NS_PRECONDITION(Type() == eDoubleValue, "wrong type");
   return GetMiscContainer()->mDoubleValue;
 }
 
 inline bool
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2130,16 +2130,49 @@ nsGenericHTMLElement::ParseAttribute(int
     }
   }
 
   return nsGenericHTMLElementBase::ParseAttribute(aNamespaceID, aAttribute,
                                                   aValue, aResult);
 }
 
 bool
+nsGenericHTMLElement::ParseBackgroundAttribute(int32_t aNamespaceID,
+                                               nsIAtom* aAttribute,
+                                               const nsAString& aValue,
+                                               nsAttrValue& aResult)
+{
+  if (aNamespaceID == kNameSpaceID_None &&
+      aAttribute == nsGkAtoms::background) {
+    // Resolve url to an absolute url
+    nsIDocument* doc = OwnerDoc();
+    nsCOMPtr<nsIURI> baseURI = GetBaseURI();
+    nsCOMPtr<nsIURI> uri;
+    nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
+        getter_AddRefs(uri), aValue, doc, baseURI);
+    if (NS_FAILED(rv)) {
+      return false;
+    }
+
+    nsString value(aValue);
+    nsRefPtr<nsStringBuffer> buffer = nsCSSValue::BufferFromString(value);
+    if (NS_UNLIKELY(!buffer)) {
+      return false;
+    }
+
+    mozilla::css::URLValue *url =
+      new mozilla::css::URLValue(buffer, baseURI, uri, NodePrincipal());
+    aResult.SetTo(url, &aValue);
+    return true;
+  }
+
+  return false;
+}
+
+bool
 nsGenericHTMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
 {
   static const MappedAttributeEntry* const map[] = {
     sCommonAttributeMap
   };
   
   return FindAttributeDependence(aAttribute, map);
 }
@@ -2734,55 +2767,26 @@ nsGenericHTMLElement::MapBackgroundInto(
   if (!(aData->mSIDs & NS_STYLE_INHERIT_BIT(Background)))
     return;
 
   nsPresContext* presContext = aData->mPresContext;
   nsCSSValue* backImage = aData->ValueForBackgroundImage();
   if (backImage->GetUnit() == eCSSUnit_Null &&
       presContext->UseDocumentColors()) {
     // background
-    const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::background);
-    if (value && value->Type() == nsAttrValue::eString) {
-      const nsString& spec = value->GetStringValue();
-      if (!spec.IsEmpty()) {
-        // Resolve url to an absolute url
-        // XXX this breaks if the HTML element has an xml:base
-        // attribute (the xml:base will not be taken into account)
-        // as well as elements with _baseHref set. We need to be able
-        // to get to the element somehow, or store the base URI in the
-        // attributes.
-        nsIDocument* doc = presContext->Document();
-        nsCOMPtr<nsIURI> uri;
-        nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
-            getter_AddRefs(uri), spec, doc, doc->GetDocBaseURI());
-        if (NS_SUCCEEDED(rv)) {
-          // Note that this should generally succeed here, due to the way
-          // |spec| is created.  Maybe we should just add an nsStringBuffer
-          // accessor on nsAttrValue?
-          nsRefPtr<nsStringBuffer> buffer = nsCSSValue::BufferFromString(spec);
-          if (NS_LIKELY(buffer)) {
-            // XXXbz it would be nice to assert that doc->NodePrincipal() is
-            // the same as the principal of the node (which we'd need to store
-            // in the mapped attrs or something?)
-            nsCSSValue::Image *img =
-              new nsCSSValue::Image(uri, buffer, doc->GetDocumentURI(),
-                                    doc->NodePrincipal(), doc);
-            if (NS_LIKELY(img)) {
-              nsCSSValueList* list = backImage->SetListValue();
-              list->mValue.SetImageValue(img);
-            }
-          }
-        }
-      }
-      else if (presContext->CompatibilityMode() == eCompatibility_NavQuirks) {
-        // in NavQuirks mode, allow the empty string to set the
-        // background to empty
-        nsCSSValueList* list = backImage->SetListValue();
-        list->mValue.SetNoneValue();
-      }
+    nsAttrValue* value =
+      const_cast<nsAttrValue*>(aAttributes->GetAttr(nsGkAtoms::background));
+    // If the value is an image, or it is a URL and we attempted a load,
+    // put it in the style tree.
+    if (value &&
+        (value->Type() == nsAttrValue::eImage ||
+         (value->Type() == nsAttrValue::eURL &&
+          value->LoadImage(presContext->Document())))) {
+      nsCSSValueList* list = backImage->SetListValue();
+      list->mValue.SetImageValue(value->GetImageValue());
     }
   }
 }
 
 void
 nsGenericHTMLElement::MapBGColorInto(const nsMappedAttributes* aAttributes,
                                      nsRuleData* aData)
 {
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -208,16 +208,21 @@ public:
 
   // Helper for setting our editable flag and notifying
   void DoSetEditableFlag(bool aEditable, bool aNotify) {
     SetEditableFlag(aEditable);
     UpdateState(aNotify);
   }
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
+                              nsIAtom* aAttribute,
+                              const nsAString& aValue,
+                              nsAttrValue& aResult);
+
+  bool ParseBackgroundAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
 
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
 
   /**
--- a/content/html/content/src/nsHTMLBodyElement.cpp
+++ b/content/html/content/src/nsHTMLBodyElement.cpp
@@ -78,19 +78,19 @@ public:
 #define FORWARDED_EVENT(name_, id_, type_, struct_)               \
     NS_IMETHOD GetOn##name_(JSContext *cx, jsval *vp);            \
     NS_IMETHOD SetOn##name_(JSContext *cx, const jsval &v);
 #include "nsEventNameList.h"
 #undef FORWARDED_EVENT
 #undef EVENT
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
-                                nsIAtom* aAttribute,
-                                const nsAString& aValue,
-                                nsAttrValue& aResult);
+                              nsIAtom* aAttribute,
+                              const nsAString& aValue,
+                              nsAttrValue& aResult);
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true);
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual already_AddRefed<nsIEditor> GetAssociatedEditor();
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
   virtual nsXPCClassInfo* GetClassInfo();
@@ -319,17 +319,20 @@ nsHTMLBodyElement::ParseAttribute(int32_
         aAttribute == nsGkAtoms::topmargin ||
         aAttribute == nsGkAtoms::bottommargin ||
         aAttribute == nsGkAtoms::leftmargin ||
         aAttribute == nsGkAtoms::rightmargin) {
       return aResult.ParseIntWithBounds(aValue, 0);
     }
   }
 
-  return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
+  return nsGenericHTMLElement::ParseBackgroundAttribute(aNamespaceID,
+                                                        aAttribute, aValue,
+                                                        aResult) ||
+         nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 void
 nsHTMLBodyElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   if (mContentStyleRule) {
     mContentStyleRule->mPart = nullptr;
--- a/content/html/content/src/nsHTMLTableCellElement.cpp
+++ b/content/html/content/src/nsHTMLTableCellElement.cpp
@@ -39,19 +39,19 @@ public:
 
   // nsIDOMHTMLElement
   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
 
   // nsIDOMHTMLTableCellElement
   NS_DECL_NSIDOMHTMLTABLECELLELEMENT
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
-                                nsIAtom* aAttribute,
-                                const nsAString& aValue,
-                                nsAttrValue& aResult);
+                              nsIAtom* aAttribute,
+                              const nsAString& aValue,
+                              nsAttrValue& aResult);
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   virtual nsXPCClassInfo* GetClassInfo();
 
@@ -280,17 +280,20 @@ nsHTMLTableCellElement::ParseAttribute(i
     if (aAttribute == nsGkAtoms::scope) {
       return aResult.ParseEnumValue(aValue, kCellScopeTable, false);
     }
     if (aAttribute == nsGkAtoms::valign) {
       return ParseTableVAlignValue(aValue, aResult);
     }
   }
 
-  return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
+  return nsGenericHTMLElement::ParseBackgroundAttribute(aNamespaceID,
+                                                        aAttribute, aValue,
+                                                        aResult) ||
+         nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 static 
 void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                            nsRuleData* aData)
 {
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
--- a/content/html/content/src/nsHTMLTableElement.cpp
+++ b/content/html/content/src/nsHTMLTableElement.cpp
@@ -905,17 +905,20 @@ nsHTMLTableElement::ParseAttribute(int32
       return aResult.ParseEnumValue(aValue, kRulesTable, false);
     }
     if (aAttribute == nsGkAtoms::hspace ||
         aAttribute == nsGkAtoms::vspace) {
       return aResult.ParseIntWithBounds(aValue, 0);
     }
   }
 
-  return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
+  return nsGenericHTMLElement::ParseBackgroundAttribute(aNamespaceID,
+                                                        aAttribute, aValue,
+                                                        aResult) ||
+         nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 
 
 static void
 MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                       nsRuleData* aData)
--- a/content/html/content/src/nsHTMLTableRowElement.cpp
+++ b/content/html/content/src/nsHTMLTableRowElement.cpp
@@ -347,17 +347,20 @@ nsHTMLTableRowElement::ParseAttribute(in
     if (aAttribute == nsGkAtoms::bgcolor) {
       return aResult.ParseColor(aValue);
     }
     if (aAttribute == nsGkAtoms::valign) {
       return ParseTableVAlignValue(aValue, aResult);
     }
   }
 
-  return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
+  return nsGenericHTMLElement::ParseBackgroundAttribute(aNamespaceID,
+                                                        aAttribute, aValue,
+                                                        aResult) ||
+         nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 static 
 void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData)
 {
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
     // height: value
--- a/content/html/content/src/nsHTMLTableSectionElement.cpp
+++ b/content/html/content/src/nsHTMLTableSectionElement.cpp
@@ -38,19 +38,19 @@ public:
 
   // nsIDOMHTMLElement
   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
 
   // nsIDOMHTMLTableSectionElement
   NS_DECL_NSIDOMHTMLTABLESECTIONELEMENT
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
-                                nsIAtom* aAttribute,
-                                const nsAString& aValue,
-                                nsAttrValue& aResult);
+                              nsIAtom* aAttribute,
+                              const nsAString& aValue,
+                              nsAttrValue& aResult);
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLTableSectionElement,
                                                      nsGenericHTMLElement)
 
@@ -233,17 +233,20 @@ nsHTMLTableSectionElement::ParseAttribut
     if (aAttribute == nsGkAtoms::bgcolor) {
       return aResult.ParseColor(aValue);
     }
     if (aAttribute == nsGkAtoms::valign) {
       return ParseTableVAlignValue(aValue, aResult);
     }
   }
 
-  return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
+  return nsGenericHTMLElement::ParseBackgroundAttribute(aNamespaceID,
+                                                        aAttribute, aValue,
+                                                        aResult) ||
+         nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 static 
 void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData)
 {
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Position)) {
     // height: value
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -7955,17 +7955,17 @@ IDBEventTargetSH::PreCreate(nsISupports 
   *aParentObj = parent ? parent : aGlobalObj;
   return NS_OK;
 }
 
 // Element helper
 
 static bool
 GetBindingURL(Element *aElement, nsIDocument *aDocument,
-              nsCSSValue::URL **aResult)
+              mozilla::css::URLValue **aResult)
 {
   // If we have a frame the frame has already loaded the binding.  And
   // otherwise, don't do anything else here unless we're dealing with
   // XUL or an HTML element that may have a plugin-related overlay
   // (i.e. object, embed, or applet).
   bool isXULorPluginElement = (aElement->IsXUL() ||
                                aElement->IsHTML(nsGkAtoms::object) ||
                                aElement->IsHTML(nsGkAtoms::embed) ||
@@ -8019,17 +8019,17 @@ nsElementSH::PreCreate(nsISupports *nati
   }
 
   if (element->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
       doc->BindingManager()->GetBinding(element)) {
     // Don't allow slim wrappers.
     return rv == NS_SUCCESS_ALLOW_SLIM_WRAPPERS ? NS_OK : rv;
   }
 
-  nsCSSValue::URL *bindingURL;
+  mozilla::css::URLValue *bindingURL;
   bool ok = GetBindingURL(element, doc, &bindingURL);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
   // Only allow slim wrappers if there's no binding.
   if (!bindingURL) {
     return rv;
   }
 
@@ -8084,17 +8084,17 @@ nsElementSH::PostCreate(nsIXPConnectWrap
 
     return NS_OK;
   }
 
   element->UnsetFlags(NODE_ATTACH_BINDING_ON_POSTCREATE);
 
   // Make sure the style context goes away _before_ we load the binding
   // since that can destroy the relevant presshell.
-  nsCSSValue::URL *bindingURL;
+  mozilla::css::URLValue *bindingURL;
   bool ok = GetBindingURL(element, doc, &bindingURL);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
   if (!bindingURL) {
     // No binding, nothing left to do here.
     return NS_OK;
   }
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/backgrounds/body-background-ref.html
@@ -0,0 +1,2 @@
+<body style="background-image: url('aqua-yellow-32x32.png')">
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/backgrounds/body-background.html
@@ -0,0 +1,2 @@
+<body background="aqua-yellow-32x32.png">
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/backgrounds/div-background-ref.html
@@ -0,0 +1,3 @@
+<div style="background-image: url('aqua-yellow-32x32.png')">
+Ohai
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/backgrounds/div-background.html
@@ -0,0 +1,3 @@
+<div background="aqua-yellow-32x32.png">
+Ohai
+</div>
--- a/layout/reftests/backgrounds/reftest.list
+++ b/layout/reftests/backgrounds/reftest.list
@@ -123,10 +123,13 @@ fails == background-size-zoom-repeat.htm
 == background-moz-default-background-color.html background-moz-default-background-color-ref.html
 
 random-if(bug685516) == fixed-bg-with-transform-outside-viewport-1.html fixed-bg-with-transform-outside-viewport-ref.html
 
 random-if(bug685516) HTTP == root-background-1.html root-background-ref.html
 random-if(bug685516) HTTP != root-background-1.html about:blank
 
 random-if(bug685516) == really-big-background.html really-big-background-ref.html
+random-if(bug685516) == body-background.html body-background-ref.html
+random-if(bug685516) == table-background.html table-background-ref.html
+random-if(bug685516) != div-background.html div-background-ref.html
 
 random-if(bug685516) == background-repeat-1-ref.html background-repeat-1.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/backgrounds/table-background-ref.html
@@ -0,0 +1,39 @@
+<body>
+<table style="background-image: url('aqua-yellow-32x32.png')">
+  <thead style="background-image: url('blue-16x20-green-16x20.png')">
+    <tr>
+      <td>
+        Foo
+      </td>
+      <td style="background-image: url('yellow-32x32.png')">
+        Bar
+      </td>
+    </tr>
+  </thead>
+  <tbody style="background-image: url('red-32x32.png')">
+    <tr>
+      <th style="background-image: url('fuchsia-32x32.png')">
+        Foo
+      </th>
+      <th>
+        Bar
+      </th>
+    </tr>
+    <tr style="background-image: url('fuchsia-32x32.png')">
+      <td>
+        Foo
+      </td>
+      <td style="background-image: url('yellow-32x32.png')">
+        Bar
+      </td>
+    </tr>
+  </tbody>
+  <tfoot style="background-image: url('yellow-32x32.png')">
+    <tr>
+      <td>
+        Baz
+      </td>
+    </tr>
+  </tfoot>
+</table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/backgrounds/table-background.html
@@ -0,0 +1,39 @@
+<body>
+<table background="aqua-yellow-32x32.png">
+  <thead background="blue-16x20-green-16x20.png">
+    <tr>
+      <td>
+        Foo
+      </td>
+      <td background="yellow-32x32.png">
+        Bar
+      </td>
+    </tr>
+  </thead>
+  <tbody background="red-32x32.png">
+    <tr>
+      <th background="fuchsia-32x32.png">
+        Foo
+      </th>
+      <th>
+        Bar
+      </th>
+    </tr>
+    <tr background="fuchsia-32x32.png">
+      <td>
+        Foo
+      </td>
+      <td background="yellow-32x32.png">
+        Bar
+      </td>
+    </tr>
+  </tbody>
+  <tfoot background="yellow-32x32.png">
+    <tr>
+      <td>
+        Baz
+      </td>
+    </tr>
+  </tfoot>
+</table>
+</body>
--- a/layout/style/ImageLoader.cpp
+++ b/layout/style/ImageLoader.cpp
@@ -103,17 +103,17 @@ ImageLoader::AssociateRequestToFrame(img
     frameSet->InsertElementAt(i, aFrame);
   }
   if (!requestSet->GreatestIndexLtEq(aRequest, i)) {
     requestSet->InsertElementAt(i, aRequest);
   }
 }
 
 void
-ImageLoader::MaybeRegisterCSSImage(nsCSSValue::Image* aImage)
+ImageLoader::MaybeRegisterCSSImage(ImageLoader::Image* aImage)
 {
   NS_ASSERTION(aImage, "This should never be null!");
 
   bool found = false;
   aImage->mRequests.GetWeak(mDocument, &found);
   if (found) {
     // This document already has a request.
     return;
@@ -134,17 +134,17 @@ ImageLoader::MaybeRegisterCSSImage(nsCSS
   mInClone = false;
 
   aImage->mRequests.Put(mDocument, request);
 
   AddImage(aImage);
 }
 
 void
-ImageLoader::DeregisterCSSImage(nsCSSValue::Image* aImage)
+ImageLoader::DeregisterCSSImage(ImageLoader::Image* aImage)
 {
   RemoveImage(aImage);
 }
 
 void
 ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
                                           nsIFrame* aFrame)
 {
@@ -214,20 +214,20 @@ ImageLoader::SetAnimationMode(uint16_t a
                aMode == imgIContainer::kDontAnimMode ||
                aMode == imgIContainer::kLoopOnceAnimMode,
                "Wrong Animation Mode is being set!");
 
   mRequestToFrameMap.EnumerateRead(SetAnimationModeEnumerator, &aMode);
 }
 
 static PLDHashOperator
-ClearImageHashSet(nsPtrHashKey<nsCSSValue::Image>* aKey, void* aClosure)
+ClearImageHashSet(nsPtrHashKey<ImageLoader::Image>* aKey, void* aClosure)
 {
   nsIDocument* doc = static_cast<nsIDocument*>(aClosure);
-  nsCSSValue::Image* image = aKey->GetKey();
+  ImageLoader::Image* image = aKey->GetKey();
 
   imgIRequest* request = image->mRequests.GetWeak(doc);
   if (request) {
     request->CancelAndForgetObserver(NS_BINDING_ABORTED);
   }
 
   image->mRequests.Remove(doc);
 
@@ -239,17 +239,17 @@ ImageLoader::ClearAll()
 {
   mRequestToFrameMap.Clear();
   mFrameToRequestMap.Clear();
   mImages.EnumerateEntries(&ClearImageHashSet, mDocument);
 }
 
 void
 ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
-                       nsIURI* aReferrer, nsCSSValue::Image* aImage)
+                       nsIURI* aReferrer, ImageLoader::Image* aImage)
 {
   NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
 
   aImage->mRequests.Put(nullptr, nullptr);
 
   if (!aURI) {
     return;
   }
@@ -279,26 +279,26 @@ ImageLoader::LoadImage(nsIURI* aURI, nsI
 
   aImage->mRequests.Put(nullptr, request);
   aImage->mRequests.Put(mDocument, clonedRequest);
 
   AddImage(aImage);
 }
 
 void
-ImageLoader::AddImage(nsCSSValue::Image* aImage)
+ImageLoader::AddImage(ImageLoader::Image* aImage)
 {
   NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
   if (!mImages.PutEntry(aImage)) {
     NS_RUNTIMEABORT("OOM");
   }
 }
 
 void
-ImageLoader::RemoveImage(nsCSSValue::Image* aImage)
+ImageLoader::RemoveImage(ImageLoader::Image* aImage)
 {
   NS_ASSERTION(mImages.Contains(aImage), "Huh?");
   mImages.RemoveEntry(aImage);
 }
 
 nsPresContext*
 ImageLoader::GetPresContext()
 {
--- a/layout/style/ImageLoader.h
+++ b/layout/style/ImageLoader.h
@@ -21,16 +21,18 @@ class nsIURI;
 class nsIPrincipal;
 
 namespace mozilla {
 namespace css {
 
 class ImageLoader : public nsStubImageDecoderObserver,
                     public imgIOnloadBlocker {
 public:
+  typedef mozilla::css::ImageValue Image;
+
   ImageLoader(nsIDocument* aDocument)
   : mDocument(aDocument),
     mInClone(false)
   {
     MOZ_ASSERT(mDocument);
 
     mRequestToFrameMap.Init();
     mFrameToRequestMap.Init();
@@ -50,52 +52,52 @@ public:
 
   // imgIContainerObserver (override nsStubImageDecoderObserver)
   NS_IMETHOD FrameChanged(imgIRequest* aRequest,
                           imgIContainer *aContainer,
                           const nsIntRect *aDirtyRect);
 
   void DropDocumentReference();
 
-  void MaybeRegisterCSSImage(nsCSSValue::Image* aImage);
-  void DeregisterCSSImage(nsCSSValue::Image* aImage);
+  void MaybeRegisterCSSImage(Image* aImage);
+  void DeregisterCSSImage(Image* aImage);
 
   void AssociateRequestToFrame(imgIRequest* aRequest,
                                nsIFrame* aFrame);
 
   void DisassociateRequestFromFrame(imgIRequest* aRequest,
                                     nsIFrame* aFrame);
 
   void DropRequestsForFrame(nsIFrame* aFrame);
 
   void SetAnimationMode(uint16_t aMode);
 
   void ClearAll();
 
   void LoadImage(nsIURI* aURI, nsIPrincipal* aPrincipal, nsIURI* aReferrer,
-                 nsCSSValue::Image* aCSSValue);
+                 Image* aCSSValue);
 
   void DestroyRequest(imgIRequest* aRequest);
 
 private:
   // We need to be able to look up the frames associated with a request (for
   // delivering notifications) and the requests associated with a frame (when
   // the frame goes away). Thus we maintain hashtables going both ways.  These
   // should always be in sync.
 
   typedef nsTArray<nsIFrame*> FrameSet;
   typedef nsTArray<nsCOMPtr<imgIRequest> > RequestSet;
-  typedef nsTHashtable<nsPtrHashKey<nsCSSValue::Image> > ImageHashSet;
+  typedef nsTHashtable<nsPtrHashKey<Image> > ImageHashSet;
   typedef nsClassHashtable<nsISupportsHashKey,
                            FrameSet> RequestToFrameMap;
   typedef nsClassHashtable<nsPtrHashKey<nsIFrame>,
                            RequestSet> FrameToRequestMap;
 
-  void AddImage(nsCSSValue::Image* aCSSImage);
-  void RemoveImage(nsCSSValue::Image* aCSSImage);
+  void AddImage(Image* aCSSImage);
+  void RemoveImage(Image* aCSSImage);
 
   nsPresContext* GetPresContext();
 
   void DoRedraw(FrameSet* aFrameSet);
 
   static PLDHashOperator
   SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue,
                              void* aClosure);
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -5036,18 +5036,18 @@ CSSParserImpl::SetValueToURL(nsCSSValue&
     NS_NOTREACHED("Codepaths that expect to parse URLs MUST pass in an "
                   "origin principal");
     return false;
   }
 
   nsRefPtr<nsStringBuffer> buffer(nsCSSValue::BufferFromString(aURL));
 
   // Note: urlVal retains its own reference to |buffer|.
-  nsCSSValue::URL *urlVal =
-    new nsCSSValue::URL(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
+  mozilla::css::URLValue *urlVal =
+    new mozilla::css::URLValue(buffer, mBaseURI, mSheetURI, mSheetPrincipal);
   aValue.SetURLValue(urlVal);
   return true;
 }
 
 /**
  * Parse the arguments of -moz-image-rect() function.
  * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>)
  */
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -68,24 +68,24 @@ nsCSSValue::nsCSSValue(const nsString& a
 nsCSSValue::nsCSSValue(nsCSSValue::Array* aValue, nsCSSUnit aUnit)
   : mUnit(aUnit)
 {
   NS_ABORT_IF_FALSE(UnitHasArrayValue(), "bad unit");
   mValue.mArray = aValue;
   mValue.mArray->AddRef();
 }
 
-nsCSSValue::nsCSSValue(nsCSSValue::URL* aValue)
+nsCSSValue::nsCSSValue(mozilla::css::URLValue* aValue)
   : mUnit(eCSSUnit_URL)
 {
   mValue.mURL = aValue;
   mValue.mURL->AddRef();
 }
 
-nsCSSValue::nsCSSValue(nsCSSValue::Image* aValue)
+nsCSSValue::nsCSSValue(mozilla::css::ImageValue* aValue)
   : mUnit(eCSSUnit_Image)
 {
   mValue.mImage = aValue;
   mValue.mImage->AddRef();
 }
 
 nsCSSValue::nsCSSValue(nsCSSValueGradient* aValue)
   : mUnit(eCSSUnit_Gradient)
@@ -359,25 +359,25 @@ void nsCSSValue::SetArrayValue(nsCSSValu
 {
   Reset();
   mUnit = aUnit;
   NS_ABORT_IF_FALSE(UnitHasArrayValue(), "bad unit");
   mValue.mArray = aValue;
   mValue.mArray->AddRef();
 }
 
-void nsCSSValue::SetURLValue(nsCSSValue::URL* aValue)
+void nsCSSValue::SetURLValue(mozilla::css::URLValue* aValue)
 {
   Reset();
   mUnit = eCSSUnit_URL;
   mValue.mURL = aValue;
   mValue.mURL->AddRef();
 }
 
-void nsCSSValue::SetImageValue(nsCSSValue::Image* aValue)
+void nsCSSValue::SetImageValue(mozilla::css::ImageValue* aValue)
 {
   Reset();
   mUnit = eCSSUnit_Image;
   mValue.mImage = aValue;
   mValue.mImage->AddRef();
 }
 
 void nsCSSValue::SetGradientValue(nsCSSValueGradient* aValue)
@@ -558,22 +558,22 @@ void nsCSSValue::SetDummyInheritValue()
 {
   Reset();
   mUnit = eCSSUnit_DummyInherit;
 }
 
 void nsCSSValue::StartImageLoad(nsIDocument* aDocument) const
 {
   NS_ABORT_IF_FALSE(eCSSUnit_URL == mUnit, "Not a URL value!");
-  nsCSSValue::Image* image =
-    new nsCSSValue::Image(mValue.mURL->GetURI(),
-                          mValue.mURL->mString,
-                          mValue.mURL->mReferrer,
-                          mValue.mURL->mOriginPrincipal,
-                          aDocument);
+  mozilla::css::ImageValue* image =
+    new mozilla::css::ImageValue(mValue.mURL->GetURI(),
+                                 mValue.mURL->mString,
+                                 mValue.mURL->mReferrer,
+                                 mValue.mURL->mOriginPrincipal,
+                                 aDocument);
   if (image) {
     nsCSSValue* writable = const_cast<nsCSSValue*>(this);
     writable->SetImageValue(image);
   }
 }
 
 bool nsCSSValue::IsNonTransparentColor() const
 {
@@ -1595,130 +1595,132 @@ nsCSSValue::Array::SizeOfIncludingThis(n
 {
   size_t n = aMallocSizeOf(this);
   for (size_t i = 0; i < mCount; i++) {
     n += mArray[i].SizeOfExcludingThis(aMallocSizeOf);
   }
   return n;
 }
 
-nsCSSValue::URL::URL(nsIURI* aURI, nsStringBuffer* aString,
-                     nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal)
+css::URLValue::URLValue(nsIURI* aURI, nsStringBuffer* aString,
+                        nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal)
   : mURI(aURI),
     mString(aString),
     mReferrer(aReferrer),
     mOriginPrincipal(aOriginPrincipal),
     mURIResolved(true)
 {
   NS_ABORT_IF_FALSE(aOriginPrincipal, "Must have an origin principal");
   mString->AddRef();
 }
 
-nsCSSValue::URL::URL(nsStringBuffer* aString, nsIURI* aBaseURI,
-                     nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal)
+css::URLValue::URLValue(nsStringBuffer* aString, nsIURI* aBaseURI,
+                        nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal)
   : mURI(aBaseURI),
     mString(aString),
     mReferrer(aReferrer),
     mOriginPrincipal(aOriginPrincipal),
     mURIResolved(false)
 {
   NS_ABORT_IF_FALSE(aOriginPrincipal, "Must have an origin principal");
   mString->AddRef();
 }
 
-nsCSSValue::URL::~URL()
+css::URLValue::~URLValue()
 {
   mString->Release();
 }
 
 bool
-nsCSSValue::URL::operator==(const URL& aOther) const
+css::URLValue::operator==(const URLValue& aOther) const
 {
   bool eq;
-  return NS_strcmp(GetBufferValue(mString),
-                   GetBufferValue(aOther.mString)) == 0 &&
+  return NS_strcmp(nsCSSValue::GetBufferValue(mString),
+                   nsCSSValue::GetBufferValue(aOther.mString)) == 0 &&
           (GetURI() == aOther.GetURI() || // handles null == null
            (mURI && aOther.mURI &&
             NS_SUCCEEDED(mURI->Equals(aOther.mURI, &eq)) &&
             eq)) &&
           (mOriginPrincipal == aOther.mOriginPrincipal ||
            (NS_SUCCEEDED(mOriginPrincipal->Equals(aOther.mOriginPrincipal,
                                                   &eq)) && eq));
 }
 
 bool
-nsCSSValue::URL::URIEquals(const URL& aOther) const
+css::URLValue::URIEquals(const URLValue& aOther) const
 {
   NS_ABORT_IF_FALSE(mURIResolved && aOther.mURIResolved,
                     "How do you know the URIs aren't null?");
   bool eq;
   // Worth comparing GetURI() to aOther.GetURI() and mOriginPrincipal to
   // aOther.mOriginPrincipal, because in the (probably common) case when this
   // value was one of the ones that in fact did not change this will be our
   // fast path to equality
   return (mURI == aOther.mURI ||
           (NS_SUCCEEDED(mURI->Equals(aOther.mURI, &eq)) && eq)) &&
          (mOriginPrincipal == aOther.mOriginPrincipal ||
           (NS_SUCCEEDED(mOriginPrincipal->Equals(aOther.mOriginPrincipal,
                                                  &eq)) && eq));
 }
 
 nsIURI*
-nsCSSValue::URL::GetURI() const
+css::URLValue::GetURI() const
 {
   if (!mURIResolved) {
     mURIResolved = true;
     // Be careful to not null out mURI before we've passed it as the base URI
     nsCOMPtr<nsIURI> newURI;
     NS_NewURI(getter_AddRefs(newURI),
-              NS_ConvertUTF16toUTF8(GetBufferValue(mString)), nullptr, mURI);
+              NS_ConvertUTF16toUTF8(nsCSSValue::GetBufferValue(mString)),
+              nullptr, mURI);
     newURI.swap(mURI);
   }
 
   return mURI;
 }
 
 size_t
-nsCSSValue::URL::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+css::URLValue::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
 
   // This string is unshared.
   n += mString->SizeOfIncludingThisMustBeUnshared(aMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mURI
   // - mReferrer
   // - mOriginPrincipal
 
   return n;
 }
 
 
-nsCSSValue::Image::Image(nsIURI* aURI, nsStringBuffer* aString,
-                         nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal,
-                         nsIDocument* aDocument)
-  : URL(aURI, aString, aReferrer, aOriginPrincipal)
+css::ImageValue::ImageValue(nsIURI* aURI, nsStringBuffer* aString,
+                            nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal,
+                            nsIDocument* aDocument)
+  : URLValue(aURI, aString, aReferrer, aOriginPrincipal)
 {
   if (aDocument->GetOriginalDocument()) {
     aDocument = aDocument->GetOriginalDocument();
   }
 
   mRequests.Init();
 
   aDocument->StyleImageLoader()->LoadImage(aURI, aOriginPrincipal, aReferrer,
                                            this);
 }
 
 static PLDHashOperator
 ClearRequestHashtable(nsISupports* aKey, nsCOMPtr<imgIRequest>& aValue,
                       void* aClosure)
 {
-  nsCSSValue::Image* image = static_cast<nsCSSValue::Image*>(aClosure);
+  mozilla::css::ImageValue* image =
+    static_cast<mozilla::css::ImageValue*>(aClosure);
   nsIDocument* doc = static_cast<nsIDocument*>(aKey);
 
 #ifdef DEBUG
   {
     nsCOMPtr<nsIDocument> slowDoc = do_QueryInterface(aKey);
     MOZ_ASSERT(slowDoc == doc);
   }
 #endif
@@ -1729,17 +1731,17 @@ ClearRequestHashtable(nsISupports* aKey,
 
   if (aValue) {
     aValue->CancelAndForgetObserver(NS_BINDING_ABORTED);
   }
 
   return PL_DHASH_REMOVE;
 }
 
-nsCSSValue::Image::~Image()
+css::ImageValue::~ImageValue()
 {
   mRequests.Enumerate(&ClearRequestHashtable, this);
 }
 
 nsCSSValueGradientStop::nsCSSValueGradientStop()
   : mLocation(eCSSUnit_None),
     mColor(eCSSUnit_Null)
 {
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -56,16 +56,89 @@ class nsPtrHashKey;
         delete (to_);                                                          \
         return nullptr;                                                         \
       }                                                                        \
       dest->member_ = clone;                                                   \
       dest = clone;                                                            \
     }                                                                          \
   }
 
+namespace mozilla {
+namespace css {
+
+struct URLValue {
+  // Methods are not inline because using an nsIPrincipal means requiring
+  // caps, which leads to REQUIRES hell, since this header is included all
+  // over.
+
+  // For both constructors aString must not be null.
+  // For both constructors aOriginPrincipal must not be null.
+  // Construct with a base URI; this will create the actual URI lazily from
+  // aString and aBaseURI.
+  URLValue(nsStringBuffer* aString, nsIURI* aBaseURI, nsIURI* aReferrer,
+           nsIPrincipal* aOriginPrincipal);
+  // Construct with the actual URI.
+  URLValue(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer,
+           nsIPrincipal* aOriginPrincipal);
+
+  ~URLValue();
+
+  bool operator==(const URLValue& aOther) const;
+
+  // URIEquals only compares URIs and principals (unlike operator==, which
+  // also compares the original strings).  URIEquals also assumes that the
+  // mURI member of both URL objects is non-null.  Do NOT call this method
+  // unless you're sure this is the case.
+  bool URIEquals(const URLValue& aOther) const;
+
+  nsIURI* GetURI() const;
+
+  size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+
+private:
+  // If mURIResolved is false, mURI stores the base URI.
+  // If mURIResolved is true, mURI stores the URI we resolve to; this may be
+  // null if the URI is invalid.
+  mutable nsCOMPtr<nsIURI> mURI;
+public:
+  nsStringBuffer* mString; // Could use nsRefPtr, but it'd add useless
+                           // null-checks; this is never null.
+  nsCOMPtr<nsIURI> mReferrer;
+  nsCOMPtr<nsIPrincipal> mOriginPrincipal;
+
+  NS_INLINE_DECL_REFCOUNTING(URLValue)
+
+private:
+  mutable bool mURIResolved;
+
+  URLValue(const URLValue& aOther) MOZ_DELETE;
+  URLValue& operator=(const URLValue& aOther) MOZ_DELETE;
+};
+
+struct ImageValue : public URLValue {
+  // Not making the constructor and destructor inline because that would
+  // force us to include imgIRequest.h, which leads to REQUIRES hell, since
+  // this header is included all over.
+  // aString must not be null.
+  ImageValue(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer,
+             nsIPrincipal* aOriginPrincipal, nsIDocument* aDocument);
+  ~ImageValue();
+
+  // Inherit operator== from URLValue
+
+  nsInterfaceHashtable<nsISupportsHashKey, imgIRequest> mRequests; 
+
+  // Override AddRef and Release to not only log ourselves correctly, but
+  // also so that we delete correctly without a virtual destructor
+  NS_INLINE_DECL_REFCOUNTING(ImageValue)
+};
+
+}
+}
+
 enum nsCSSUnit {
   eCSSUnit_Null         = 0,      // (n/a) null unit, value is not specified
   eCSSUnit_Auto         = 1,      // (n/a) value is algorithmic
   eCSSUnit_Inherit      = 2,      // (n/a) value is inherited
   eCSSUnit_Initial      = 3,      // (n/a) value is default UA value
   eCSSUnit_None         = 4,      // (n/a) value is none
   eCSSUnit_Normal       = 5,      // (n/a) value is normal (algorithmic, different than auto)
   eCSSUnit_System_Font  = 6,      // (n/a) value is -moz-use-system-font
@@ -177,35 +250,33 @@ struct nsCSSValuePairList_heap;
 struct nsCSSValueTriplet;
 struct nsCSSValueTriplet_heap;
 
 class nsCSSValue {
 public:
   struct Array;
   friend struct Array;
 
-  struct URL;
-  friend struct URL;
+  friend struct mozilla::css::URLValue;
 
-  struct Image;
-  friend struct Image;
+  friend struct mozilla::css::ImageValue;
 
   // for valueless units only (null, auto, inherit, none, all, normal)
   explicit nsCSSValue(nsCSSUnit aUnit = eCSSUnit_Null)
     : mUnit(aUnit)
   {
     NS_ABORT_IF_FALSE(aUnit <= eCSSUnit_DummyInherit, "not a valueless unit");
   }
 
   nsCSSValue(int32_t aValue, nsCSSUnit aUnit);
   nsCSSValue(float aValue, nsCSSUnit aUnit);
   nsCSSValue(const nsString& aValue, nsCSSUnit aUnit);
   nsCSSValue(Array* aArray, nsCSSUnit aUnit);
-  explicit nsCSSValue(URL* aValue);
-  explicit nsCSSValue(Image* aValue);
+  explicit nsCSSValue(mozilla::css::URLValue* aValue);
+  explicit nsCSSValue(mozilla::css::ImageValue* aValue);
   explicit nsCSSValue(nsCSSValueGradient* aValue);
   nsCSSValue(const nsCSSValue& aCopy);
   ~nsCSSValue() { Reset(); }
 
   nsCSSValue&  operator=(const nsCSSValue& aCopy);
   bool        operator==(const nsCSSValue& aOther) const;
 
   bool operator!=(const nsCSSValue& aOther) const
@@ -344,25 +415,25 @@ public:
   inline const nsCSSValueList* GetListValue() const;
 
   inline nsCSSValuePairList* GetPairListValue();
   inline const nsCSSValuePairList* GetPairListValue() const;
 
   inline nsCSSValueTriplet& GetTripletValue();
   inline const nsCSSValueTriplet& GetTripletValue() const;
 
-  URL* GetURLStructValue() const
+  mozilla::css::URLValue* GetURLStructValue() const
   {
     // Not allowing this for Image values, because if the caller takes
     // a ref to them they won't be able to delete them properly.
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_URL, "not a URL value");
     return mValue.mURL;
   }
 
-  Image* GetImageStructValue() const
+  mozilla::css::ImageValue* GetImageStructValue() const
   {
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Image, "not an Image value");
     return mValue.mImage;
   }
 
   const PRUnichar* GetOriginalURLValue() const
   {
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_URL || mUnit == eCSSUnit_Image,
@@ -390,18 +461,18 @@ private:
 
 public:
   void SetIntValue(int32_t aValue, nsCSSUnit aUnit);
   void SetPercentValue(float aValue);
   void SetFloatValue(float aValue, nsCSSUnit aUnit);
   void SetStringValue(const nsString& aValue, nsCSSUnit aUnit);
   void SetColorValue(nscolor aValue);
   void SetArrayValue(nsCSSValue::Array* aArray, nsCSSUnit aUnit);
-  void SetURLValue(nsCSSValue::URL* aURI);
-  void SetImageValue(nsCSSValue::Image* aImage);
+  void SetURLValue(mozilla::css::URLValue* aURI);
+  void SetImageValue(mozilla::css::ImageValue* aImage);
   void SetGradientValue(nsCSSValueGradient* aGradient);
   void SetPairValue(const nsCSSValuePair* aPair);
   void SetPairValue(const nsCSSValue& xValue, const nsCSSValue& yValue);
   void SetDependentListValue(nsCSSValueList* aList);
   void SetDependentPairListValue(nsCSSValuePairList* aList);
   void SetTripletValue(const nsCSSValueTriplet* aTriplet);
   void SetTripletValue(const nsCSSValue& xValue, const nsCSSValue& yValue, const nsCSSValue& zValue);
   void SetAutoValue();
@@ -429,100 +500,33 @@ public:
 
   // Returns an already addrefed buffer.  Can return null on allocation
   // failure.
   static already_AddRefed<nsStringBuffer>
     BufferFromString(const nsString& aValue);
 
   size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
 
-  struct URL {
-    // Methods are not inline because using an nsIPrincipal means requiring
-    // caps, which leads to REQUIRES hell, since this header is included all
-    // over.
-
-    // For both constructors aString must not be null.
-    // For both constructors aOriginPrincipal must not be null.
-    // Construct with a base URI; this will create the actual URI lazily from
-    // aString and aBaseURI.
-    URL(nsStringBuffer* aString, nsIURI* aBaseURI, nsIURI* aReferrer,
-        nsIPrincipal* aOriginPrincipal);
-    // Construct with the actual URI.
-    URL(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer,
-        nsIPrincipal* aOriginPrincipal);
-
-    ~URL();
-
-    bool operator==(const URL& aOther) const;
-
-    // URIEquals only compares URIs and principals (unlike operator==, which
-    // also compares the original strings).  URIEquals also assumes that the
-    // mURI member of both URL objects is non-null.  Do NOT call this method
-    // unless you're sure this is the case.
-    bool URIEquals(const URL& aOther) const;
-
-    nsIURI* GetURI() const;
-
-    size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
-
-  private:
-    // If mURIResolved is false, mURI stores the base URI.
-    // If mURIResolved is true, mURI stores the URI we resolve to; this may be
-    // null if the URI is invalid.
-    mutable nsCOMPtr<nsIURI> mURI;
-  public:
-    nsStringBuffer* mString; // Could use nsRefPtr, but it'd add useless
-                             // null-checks; this is never null.
-    nsCOMPtr<nsIURI> mReferrer;
-    nsCOMPtr<nsIPrincipal> mOriginPrincipal;
-
-    NS_INLINE_DECL_REFCOUNTING(nsCSSValue::URL)
-
-  private:
-    mutable bool mURIResolved;
-
-    URL(const URL& aOther) MOZ_DELETE;
-    URL& operator=(const URL& aOther) MOZ_DELETE;
-  };
-
-  struct Image : public URL {
-    // Not making the constructor and destructor inline because that would
-    // force us to include imgIRequest.h, which leads to REQUIRES hell, since
-    // this header is included all over.
-    // aString must not be null.
-    Image(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer,
-          nsIPrincipal* aOriginPrincipal, nsIDocument* aDocument);
-    ~Image();
-
-    // Inherit operator== from nsCSSValue::URL
-
-    nsInterfaceHashtable<nsISupportsHashKey, imgIRequest> mRequests; 
-
-    // Override AddRef and Release to not only log ourselves correctly, but
-    // also so that we delete correctly without a virtual destructor
-    NS_INLINE_DECL_REFCOUNTING(nsCSSValue::Image)
-  };
-
 private:
   static const PRUnichar* GetBufferValue(nsStringBuffer* aBuffer) {
     return static_cast<PRUnichar*>(aBuffer->Data());
   }
 
 protected:
   nsCSSUnit mUnit;
   union {
     int32_t    mInt;
     float      mFloat;
     // Note: the capacity of the buffer may exceed the length of the string.
     // If we're of a string type, mString is not null.
     nsStringBuffer* mString;
     nscolor    mColor;
     Array*     mArray;
-    URL*       mURL;
-    Image*     mImage;
+    mozilla::css::URLValue* mURL;
+    mozilla::css::ImageValue* mImage;
     nsCSSValueGradient* mGradient;
     nsCSSValuePair_heap* mPair;
     nsCSSRect_heap* mRect;
     nsCSSValueTriplet_heap* mTriplet;
     nsCSSValueList_heap* mList;
     nsCSSValueList* mListDependent;
     nsCSSValuePairList_heap* mPairList;
     nsCSSValuePairList* mPairListDependent;
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4653,17 +4653,17 @@ nsRuleNode::ComputeDisplayData(void* aSt
   SetDiscrete(*aRuleData->ValueForAppearance(),
               display->mAppearance, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentDisplay->mAppearance,
               NS_THEME_NONE, 0, 0, 0, 0);
 
   // binding: url, none, inherit
   const nsCSSValue* bindingValue = aRuleData->ValueForBinding();
   if (eCSSUnit_URL == bindingValue->GetUnit()) {
-    nsCSSValue::URL* url = bindingValue->GetURLStructValue();
+    mozilla::css::URLValue* url = bindingValue->GetURLStructValue();
     NS_ASSERTION(url, "What's going on here?");
 
     if (NS_LIKELY(url->GetURI())) {
       display->mBinding = url;
     } else {
       display->mBinding = nullptr;
     }
   }
--- a/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -3119,21 +3119,21 @@ nsStyleAnimation::ExtractComputedValue(n
           NS_WARNING("Null paint server");
           return false;
         }
         nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
         nsRefPtr<nsStringBuffer> uriAsStringBuffer =
           GetURIAsUtf16StringBuffer(paint.mPaint.mPaintServer);
         NS_ENSURE_TRUE(!!uriAsStringBuffer, false);
         nsIDocument* doc = aStyleContext->PresContext()->Document();
-        nsRefPtr<nsCSSValue::URL> url =
-          new nsCSSValue::URL(paint.mPaint.mPaintServer,
-                              uriAsStringBuffer,
-                              doc->GetDocumentURI(),
-                              doc->NodePrincipal());
+        nsRefPtr<mozilla::css::URLValue> url =
+          new mozilla::css::URLValue(paint.mPaint.mPaintServer,
+                                     uriAsStringBuffer,
+                                     doc->GetDocumentURI(),
+                                     doc->NodePrincipal());
         pair->mXValue.SetURLValue(url);
         pair->mYValue.SetColorValue(paint.mFallbackColor);
         aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
                                                     eUnit_CSSValuePair);
         return true;
       }
       NS_ABORT_IF_FALSE(paint.mType == eStyleSVGPaintType_None,
           "Unexpected SVG paint type");
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -46,17 +46,17 @@ static bool EqualURIs(nsIURI *aURI1, nsI
 {
   bool eq;
   return aURI1 == aURI2 ||    // handle null==null, and optimize
          (aURI1 && aURI2 &&
           NS_SUCCEEDED(aURI1->Equals(aURI2, &eq)) && // not equal on fail
           eq);
 }
 
-static bool EqualURIs(nsCSSValue::URL *aURI1, nsCSSValue::URL *aURI2)
+static bool EqualURIs(mozilla::css::URLValue *aURI1, mozilla::css::URLValue *aURI2)
 {
   return aURI1 == aURI2 ||    // handle null==null, and optimize
          (aURI1 && aURI2 && aURI1->URIEquals(*aURI2));
 }
 
 static bool EqualImages(imgIRequest *aImage1, imgIRequest* aImage2)
 {
   if (aImage1 == aImage2) {
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1551,17 +1551,17 @@ struct nsStyleDisplay {
   nsChangeHint CalcDifference(const nsStyleDisplay& aOther) const;
 #ifdef DEBUG
   static nsChangeHint MaxDifference();
 #endif
   static bool ForceCompare() { return true; }
 
   // We guarantee that if mBinding is non-null, so are mBinding->GetURI() and
   // mBinding->mOriginPrincipal.
-  nsRefPtr<nsCSSValue::URL> mBinding;    // [reset]
+  nsRefPtr<mozilla::css::URLValue> mBinding;    // [reset]
   nsRect  mClip;                // [reset] offsets from upper-left border edge
   float   mOpacity;             // [reset]
   uint8_t mDisplay;             // [reset] see nsStyleConsts.h NS_STYLE_DISPLAY_*
   uint8_t mOriginalDisplay;     // [reset] saved mDisplay for position:absolute/fixed
                                 //         and float:left/right; otherwise equal
                                 //         to mDisplay
   uint8_t mAppearance;          // [reset]
   uint8_t mPosition;            // [reset] see nsStyleConsts.h