Bug 1288302 - Part 5: Make nsStyleImage use nsStyleImageRequest. r=bholley draft
authorCameron McCormack <cam@mcc.id.au>
Mon, 03 Oct 2016 11:53:05 +0800
changeset 426807 5f18410cb69bc5c25a25a6ca72125fee3f9bcf9a
parent 426806 d878d0020a5bacec8d84222c07ef161eaef05cd9
child 426808 50526e56affdf8f210913945c1bb2114b9ba2807
push id32806
push userbmo:cam@mcc.id.au
push dateWed, 19 Oct 2016 05:46:23 +0000
reviewersbholley
bugs1288302
milestone52.0a1
Bug 1288302 - Part 5: Make nsStyleImage use nsStyleImageRequest. r=bholley This makes background-image, mask-image and border-image settable from Servo. Since imgRequestProxy resolution in nsStyleImages can now happen later than at computed value setting time, and that resolution can fail, nsStyleImage::GetImageData() might now return null. So all of the users of nsStyleImage now null check its result. MozReview-Commit-ID: FMRUrC3SfOs
layout/base/nsCSSRendering.cpp
layout/base/nsDisplayList.cpp
layout/generic/nsFrame.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -662,36 +662,27 @@ nsCSSRendering::PaintBorder(nsPresContex
   // same amount of time whether or not it's true.
   if (!styleIfVisited) {
     return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                                       aDirtyRect, aBorderArea, *styleBorder,
                                       aStyleContext, aFlags, aSkipSides);
   }
 
   nsStyleBorder newStyleBorder(*styleBorder);
-  // We could do something fancy to avoid the TrackImage/UntrackImage
-  // work, but it doesn't seem worth it.  (We need to call TrackImage
-  // since we're not going through nsRuleNode::ComputeBorderData.)
-  newStyleBorder.TrackImage(aPresContext->Document()->ImageTracker());
 
   NS_FOR_CSS_SIDES(side) {
     nscolor color = aStyleContext->GetVisitedDependentColor(
       nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]);
     newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
   }
   DrawResult result =
     PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
                                aDirtyRect, aBorderArea, newStyleBorder,
                                aStyleContext, aFlags, aSkipSides);
 
-  // We could do something fancy to avoid the TrackImage/UntrackImage
-  // work, but it doesn't seem worth it.  (We need to call UntrackImage
-  // since we're not going through nsStyleBorder::Destroy.)
-  newStyleBorder.UntrackImage(aPresContext->Document()->ImageTracker());
-
   return result;
 }
 
 DrawResult
 nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
                                            nsRenderingContext& aRenderingContext,
                                            nsIFrame* aForFrame,
                                            const nsRect& aDirtyRect,
@@ -5027,26 +5018,31 @@ ShouldTreatAsCompleteDueToSyncDecode(con
   if (!(aFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES)) {
     return false;
   }
 
   if (aImage->GetType() != eStyleImageType_Image) {
     return false;
   }
 
+  imgRequestProxy* req = aImage->GetImageData();
+  if (!req) {
+    return false;
+  }
+
   uint32_t status = 0;
-  if (NS_FAILED(aImage->GetImageData()->GetImageStatus(&status))) {
+  if (NS_FAILED(req->GetImageStatus(&status))) {
     return false;
   }
 
   if (status & imgIRequest::STATUS_ERROR) {
     // The image is "complete" since it's a corrupt image. If we created an
     // imgIContainer at all, return true.
     nsCOMPtr<imgIContainer> image;
-    aImage->GetImageData()->GetImage(getter_AddRefs(image));
+    req->GetImage(getter_AddRefs(image));
     return bool(image);
   }
 
   if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
     // We must have loaded all of the image's data and the size must be
     // available, or else sync decoding won't be able to decode the image.
     return false;
   }
@@ -5073,18 +5069,19 @@ nsImageRenderer::PrepareImage()
     if (!mImage->IsComplete() &&
         !ShouldTreatAsCompleteDueToSyncDecode(mImage, mFlags)) {
       mPrepareResult = DrawResult::NOT_READY;
       return false;
     }
   }
 
   switch (mType) {
-    case eStyleImageType_Image:
-    {
+    case eStyleImageType_Image: {
+      MOZ_ASSERT(mImage->GetImageData(),
+                 "must have image data, since we checked IsEmpty above");
       nsCOMPtr<imgIContainer> srcImage;
       DebugOnly<nsresult> rv =
         mImage->GetImageData()->GetImage(getter_AddRefs(srcImage));
       MOZ_ASSERT(NS_SUCCEEDED(rv) && srcImage,
                  "If GetImage() is failing, mImage->IsComplete() "
                  "should have returned false");
 
       if (!mImage->GetCropRect()) {
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2859,17 +2859,19 @@ nsDisplayBackgroundImage::ShouldCreateOw
   }
 
   if (nsLayoutUtils::AnimatedImageLayersEnabled() && mBackgroundStyle) {
     const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer];
     const nsStyleImage* image = &layer.mImage;
     if (image->GetType() == eStyleImageType_Image) {
       imgIRequest* imgreq = image->GetImageData();
       nsCOMPtr<imgIContainer> image;
-      if (NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) && image) {
+      if (imgreq &&
+          NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) &&
+          image) {
         bool animated = false;
         if (NS_SUCCEEDED(image->GetAnimated(&animated)) && animated) {
           return WHENEVER_POSSIBLE;
         }
       }
     }
   }
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -787,33 +787,36 @@ AddAndRemoveImageAssociations(nsFrame* a
       if (i >= aNewLayers->mImageCount ||
           !aOldLayers->mLayers[i].mImage.ImageDataEquals(
             aNewLayers->mLayers[i].mImage)) {
         const nsStyleImage& oldImage = aOldLayers->mLayers[i].mImage;
         if (oldImage.GetType() != eStyleImageType_Image) {
           continue;
         }
 
-        imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(),
-                                                   aFrame);
+        if (imgRequestProxy* req = oldImage.GetImageData()) {
+          imageLoader->DisassociateRequestFromFrame(req, aFrame);
+        }
       }
     }
   }
 
   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aNewLayers)) {
     // If there is an image in newBG that's not in oldBG, add it.
     if (!aOldLayers || i >= aOldLayers->mImageCount ||
         !aNewLayers->mLayers[i].mImage.ImageDataEquals(
           aOldLayers->mLayers[i].mImage)) {
       const nsStyleImage& newImage = aNewLayers->mLayers[i].mImage;
       if (newImage.GetType() != eStyleImageType_Image) {
         continue;
       }
 
-      imageLoader->AssociateRequestToFrame(newImage.GetImageData(), aFrame);
+      if (imgRequestProxy* req = newImage.GetImageData()) {
+        imageLoader->AssociateRequestToFrame(req, aFrame);
+      }
     }
   }
 }
 
 // Subclass hook for style post processing
 /* virtual */ void
 nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
@@ -4295,17 +4298,21 @@ nsIFrame::ContentOffsets nsFrame::CalcCo
 
 void
 nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext)
 {
   if (aImage.GetType() != eStyleImageType_Image) {
     return;
   }
 
-  imgIRequest *req = aImage.GetImageData();
+  imgRequestProxy* req = aImage.GetImageData();
+  if (!req) {
+    return;
+  }
+
   mozilla::css::ImageLoader* loader =
     aPresContext->Document()->StyleImageLoader();
 
   // If this fails there's not much we can do ...
   loader->AssociateRequestToFrame(req, this);
 }
 
 nsresult
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2077,17 +2077,25 @@ nsComputedDOMStyle::GetImageRectString(n
 
 void
 nsComputedDOMStyle::SetValueToStyleImage(const nsStyleImage& aStyleImage,
                                          nsROCSSPrimitiveValue* aValue)
 {
   switch (aStyleImage.GetType()) {
     case eStyleImageType_Image:
     {
-      imgIRequest *req = aStyleImage.GetImageData();
+      imgIRequest* req = aStyleImage.GetImageData();
+      if (!req) {
+        // XXXheycam If we had some problem resolving the imgRequestProxy,
+        // maybe we should just use the URL stored in the nsStyleImage's
+        // mImageValue?
+        aValue->SetIdent(eCSSKeyword_none);
+        break;
+      }
+
       nsCOMPtr<nsIURI> uri;
       req->GetURI(getter_AddRefs(uri));
 
       const UniquePtr<nsStyleSides>& cropRect = aStyleImage.GetCropRect();
       if (cropRect) {
         nsAutoString imageRectString;
         GetImageRectString(uri, *cropRect, imageRectString);
         aValue->SetString(imageRectString);
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -129,16 +129,36 @@ SetImageRequest(function<void(imgRequest
                 const nsCSSValue& aValue)
 {
   RefPtr<imgRequestProxy> req =
     aValue.GetPossiblyStaticImageValue(aPresContext->Document(),
                                        aPresContext);
   aCallback(req);
 }
 
+static void
+SetStyleImageRequest(function<void(nsStyleImageRequest*)> aCallback,
+                     nsPresContext* aPresContext,
+                     const nsCSSValue& aValue,
+                     nsStyleImageRequest::Mode aModeFlags =
+                       nsStyleImageRequest::Mode::Track |
+                       nsStyleImageRequest::Mode::Lock)
+{
+  SetImageRequest([&](imgRequestProxy* aProxy) {
+    RefPtr<nsStyleImageRequest> request;
+    if (aProxy) {
+      css::ImageValue* imageValue = aValue.GetImageStructValue();
+      ImageTracker* imageTracker = aPresContext->Document()->ImageTracker();
+      request =
+        new nsStyleImageRequest(aModeFlags, aProxy, imageValue, imageTracker);
+    }
+    aCallback(request);
+  }, aPresContext, aValue);
+}
+
 template<typename ReferenceBox>
 static void
 SetStyleShapeSourceToCSSValue(StyleShapeSource<ReferenceBox>* aShapeSource,
                               const nsCSSValue* aValue,
                               nsStyleContext* aStyleContext,
                               nsPresContext* aPresContext,
                               RuleNodeCacheConditions& aConditions);
 
@@ -1259,18 +1279,18 @@ static void SetStyleImageToImageRect(nsS
              aValue.EqualsFunction(eCSSKeyword__moz_image_rect),
              "the value is not valid -moz-image-rect()");
 
   nsCSSValue::Array* arr = aValue.GetArrayValue();
   MOZ_ASSERT(arr && arr->Count() == 6, "invalid number of arguments");
 
   // <uri>
   if (arr->Item(1).GetUnit() == eCSSUnit_Image) {
-    SetImageRequest([&](imgRequestProxy* req) {
-      aResult.SetImageData(req);
+    SetStyleImageRequest([&](nsStyleImageRequest* req) {
+      aResult.SetImageRequest(do_AddRef(req));
     }, aStyleContext->PresContext(), arr->Item(1));
   } else {
     NS_WARNING("nsCSSValue::Image::Image() failed?");
   }
 
   // <top>, <right>, <bottom>, <left>
   nsStyleSides cropRect;
   NS_FOR_CSS_SIDES(side) {
@@ -1295,18 +1315,18 @@ static void SetStyleImage(nsStyleContext
   if (aValue.GetUnit() == eCSSUnit_Null) {
     return;
   }
 
   aResult.SetNull();
 
   switch (aValue.GetUnit()) {
     case eCSSUnit_Image:
-      SetImageRequest([&](imgRequestProxy* req) {
-        aResult.SetImageData(req);
+      SetStyleImageRequest([&](nsStyleImageRequest* req) {
+        aResult.SetImageRequest(do_AddRef(req));
       }, aStyleContext->PresContext(), aValue);
       break;
     case eCSSUnit_Function:
       if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) {
         SetStyleImageToImageRect(aStyleContext, aValue, aResult);
       } else {
         NS_NOTREACHED("-moz-image-rect() is the only expected function");
       }
@@ -7320,19 +7340,16 @@ nsRuleNode::ComputeBackgroundData(void* 
                         initialSize, parentBG->mImage.mSizeCount,
                         bg->mImage.mSizeCount, maxItemCount, rebuild,
                         conditions);
 
   if (rebuild) {
     FillAllBackgroundLists(bg->mImage, maxItemCount);
   }
 
-  // Now that the dust has settled, register the images with the document
-  bg->mImage.TrackImages(aContext->PresContext()->Document()->ImageTracker());
-
   COMPUTE_END_RESET(Background, bg)
 }
 
 const void*
 nsRuleNode::ComputeMarginData(void* aStartStruct,
                               const nsRuleData* aRuleData,
                               nsStyleContext* aContext,
                               nsRuleNode* aHighestNode,
@@ -7709,18 +7726,16 @@ nsRuleNode::ComputeBorderData(void* aSta
 
   SetValue(borderImageRepeat.mYValue,
            border->mBorderImageRepeatV,
            conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentBorder->mBorderImageRepeatV,
            NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH);
 
-  border->TrackImage(aContext->PresContext()->Document()->ImageTracker());
-
   COMPUTE_END_RESET(Border, border)
 }
 
 const void*
 nsRuleNode::ComputePaddingData(void* aStartStruct,
                                const nsRuleData* aRuleData,
                                nsStyleContext* aContext,
                                nsRuleNode* aHighestNode,
@@ -10047,18 +10062,16 @@ nsRuleNode::ComputeSVGResetData(void* aS
     svgReset->mMask.mLayers[0].mSourceURI.SetNull();
   } else if (eCSSUnit_Inherit == maskValue->GetUnit()) {
     conditions.SetUncacheable();
     svgReset->mMask.mLayers[0].mSourceURI =
       parentSVGReset->mMask.mLayers[0].mSourceURI;
   }
 #endif
 
-  svgReset->mMask.TrackImages(aContext->PresContext()->Document()->ImageTracker());
-
   COMPUTE_END_RESET(SVGReset, svgReset)
 }
 
 const void*
 nsRuleNode::ComputeVariablesData(void* aStartStruct,
                                  const nsRuleData* aRuleData,
                                  nsStyleContext* aContext,
                                  nsRuleNode* aHighestNode,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -446,16 +446,25 @@ nsStyleBorder::~nsStyleBorder()
   if (mBorderColors) {
     for (int32_t i = 0; i < 4; i++) {
       delete mBorderColors[i];
     }
     delete [] mBorderColors;
   }
 }
 
+void
+nsStyleBorder::FinishStyle(nsPresContext* aPresContext)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
+
+  mBorderImageSource.ResolveImage(aPresContext);
+}
+
 nsMargin
 nsStyleBorder::GetImageOutset() const
 {
   // We don't check whether there is a border-image (which is OK since
   // the initial values yields 0 outset) so that we don't have to
   // reflow to update overflow areas when an image loads.
   nsMargin outset;
   NS_FOR_CSS_SIDES(s) {
@@ -476,18 +485,16 @@ nsStyleBorder::GetImageOutset() const
     outset.Side(s) = value;
   }
   return outset;
 }
 
 void
 nsStyleBorder::Destroy(nsPresContext* aContext)
 {
-  UntrackImage(aContext->Document()->ImageTracker());
-
   this->~nsStyleBorder();
   aContext->PresShell()->
     FreeByObjectID(eArenaObjectID_nsStyleBorder, this);
 }
 
 nsChangeHint
 nsStyleBorder::CalcDifference(const nsStyleBorder& aNewData) const
 {
@@ -1181,23 +1188,30 @@ nsStyleSVGReset::nsStyleSVGReset(const n
   , mMaskType(aSource.mMaskType)
 {
   MOZ_COUNT_CTOR(nsStyleSVGReset);
 }
 
 void
 nsStyleSVGReset::Destroy(nsPresContext* aContext)
 {
-  mMask.UntrackImages(aContext->Document()->ImageTracker());
-
   this->~nsStyleSVGReset();
   aContext->PresShell()->
     FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this);
 }
 
+void
+nsStyleSVGReset::FinishStyle(nsPresContext* aPresContext)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
+
+  mMask.ResolveImages(aPresContext);
+}
+
 nsChangeHint
 nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aNewData) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
   if (mClipPath != aNewData.mClipPath) {
     hint |= nsChangeHint_UpdateEffects |
             nsChangeHint_RepaintFrame;
@@ -2093,37 +2107,31 @@ CachedBorderImageData::GetSubImage(uint8
 
 // --------------------
 // nsStyleImage
 //
 
 nsStyleImage::nsStyleImage()
   : mType(eStyleImageType_Null)
   , mCropRect(nullptr)
-#ifdef DEBUG
-  , mImageTracked(false)
-#endif
 {
   MOZ_COUNT_CTOR(nsStyleImage);
 }
 
 nsStyleImage::~nsStyleImage()
 {
   MOZ_COUNT_DTOR(nsStyleImage);
   if (mType != eStyleImageType_Null) {
     SetNull();
   }
 }
 
 nsStyleImage::nsStyleImage(const nsStyleImage& aOther)
   : mType(eStyleImageType_Null)
   , mCropRect(nullptr)
-#ifdef DEBUG
-  , mImageTracked(false)
-#endif
 {
   // We need our own copy constructor because we don't want
   // to copy the reference count
   MOZ_COUNT_CTOR(nsStyleImage);
   DoCopy(aOther);
 }
 
 nsStyleImage&
@@ -2137,102 +2145,64 @@ nsStyleImage::operator=(const nsStyleIma
 }
 
 void
 nsStyleImage::DoCopy(const nsStyleImage& aOther)
 {
   SetNull();
 
   if (aOther.mType == eStyleImageType_Image) {
-    SetImageData(aOther.mImage);
+    SetImageRequest(do_AddRef(aOther.mImage));
   } else if (aOther.mType == eStyleImageType_Gradient) {
     SetGradientData(aOther.mGradient);
   } else if (aOther.mType == eStyleImageType_Element) {
     SetElementId(aOther.mElementId);
   }
 
   UniquePtr<nsStyleSides> cropRectCopy;
   if (aOther.mCropRect) {
     cropRectCopy = MakeUnique<nsStyleSides>(*aOther.mCropRect.get());
   }
   SetCropRect(Move(cropRectCopy));
 }
 
 void
 nsStyleImage::SetNull()
 {
-  MOZ_ASSERT(!mImageTracked,
-             "Calling SetNull() with image tracked!");
-
   if (mType == eStyleImageType_Gradient) {
     mGradient->Release();
   } else if (mType == eStyleImageType_Image) {
     NS_RELEASE(mImage);
   } else if (mType == eStyleImageType_Element) {
     free(mElementId);
   }
 
   mType = eStyleImageType_Null;
   mCropRect = nullptr;
 }
 
 void
-nsStyleImage::SetImageData(imgRequestProxy* aImage)
+nsStyleImage::SetImageRequest(already_AddRefed<nsStyleImageRequest> aImage)
 {
-  MOZ_ASSERT(!mImageTracked,
-             "Setting a new image without untracking the old one!");
-
-  NS_IF_ADDREF(aImage);
+  RefPtr<nsStyleImageRequest> image = aImage;
 
   if (mType != eStyleImageType_Null) {
     SetNull();
   }
 
-  if (aImage) {
-    mImage = aImage;
+  if (image) {
+    mImage = image.forget().take();
     mType = eStyleImageType_Image;
   }
   if (mCachedBIData) {
     mCachedBIData->PurgeCachedImages();
   }
 }
 
 void
-nsStyleImage::TrackImage(ImageTracker* aImageTracker)
-{
-  // Sanity
-  MOZ_ASSERT(!mImageTracked, "Already tracking image!");
-  MOZ_ASSERT(mType == eStyleImageType_Image,
-             "Can't track image when there isn't one!");
-
-  aImageTracker->Add(mImage);
-
-  // Mark state
-#ifdef DEBUG
-  mImageTracked = true;
-#endif
-}
-
-void
-nsStyleImage::UntrackImage(ImageTracker* aImageTracker)
-{
-  // Sanity
-  MOZ_ASSERT(mImageTracked, "Image not tracked!");
-  MOZ_ASSERT(mType == eStyleImageType_Image,
-             "Can't untrack image when there isn't one!");
-
-  aImageTracker->Remove(mImage);
-
-  // Mark state
-#ifdef DEBUG
-  mImageTracked = false;
-#endif
-}
-
-void
 nsStyleImage::SetGradientData(nsStyleGradient* aGradient)
 {
   if (aGradient) {
     aGradient->AddRef();
   }
 
   if (mType != eStyleImageType_Null) {
     SetNull();
@@ -2286,18 +2256,23 @@ ConvertToPixelCoord(const nsStyleCoord& 
 bool
 nsStyleImage::ComputeActualCropRect(nsIntRect& aActualCropRect,
                                     bool* aIsEntireImage) const
 {
   if (mType != eStyleImageType_Image) {
     return false;
   }
 
+  imgRequestProxy* req = GetImageData();
+  if (!req) {
+    return false;
+  }
+
   nsCOMPtr<imgIContainer> imageContainer;
-  mImage->GetImage(getter_AddRefs(imageContainer));
+  req->GetImage(getter_AddRefs(imageContainer));
   if (!imageContainer) {
     return false;
   }
 
   nsIntSize imageSize;
   imageContainer->GetWidth(&imageSize.width);
   imageContainer->GetHeight(&imageSize.height);
   if (imageSize.width <= 0 || imageSize.height <= 0) {
@@ -2319,17 +2294,21 @@ nsStyleImage::ComputeActualCropRect(nsIn
   }
   return true;
 }
 
 nsresult
 nsStyleImage::StartDecoding() const
 {
   if (mType == eStyleImageType_Image) {
-    return mImage->StartDecoding();
+    imgRequestProxy* req = GetImageData();
+    if (!req) {
+      return NS_ERROR_FAILURE;
+    }
+    return req->StartDecoding();
   }
   return NS_OK;
 }
 
 bool
 nsStyleImage::IsOpaque() const
 {
   if (!IsComplete()) {
@@ -2340,19 +2319,20 @@ nsStyleImage::IsOpaque() const
     return mGradient->IsOpaque();
   }
 
   if (mType == eStyleImageType_Element) {
     return false;
   }
 
   MOZ_ASSERT(mType == eStyleImageType_Image, "unexpected image type");
+  MOZ_ASSERT(GetImageData(), "should've returned earlier above");
 
   nsCOMPtr<imgIContainer> imageContainer;
-  mImage->GetImage(getter_AddRefs(imageContainer));
+  GetImageData()->GetImage(getter_AddRefs(imageContainer));
   MOZ_ASSERT(imageContainer, "IsComplete() said image container is ready");
 
   // Check if the crop region of the image is opaque.
   if (imageContainer->WillDrawOpaqueNow()) {
     if (!mCropRect) {
       return true;
     }
 
@@ -2371,20 +2351,23 @@ bool
 nsStyleImage::IsComplete() const
 {
   switch (mType) {
     case eStyleImageType_Null:
       return false;
     case eStyleImageType_Gradient:
     case eStyleImageType_Element:
       return true;
-    case eStyleImageType_Image:
-    {
+    case eStyleImageType_Image: {
+      imgRequestProxy* req = GetImageData();
+      if (!req) {
+        return false;
+      }
       uint32_t status = imgIRequest::STATUS_ERROR;
-      return NS_SUCCEEDED(mImage->GetImageStatus(&status)) &&
+      return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
              (status & imgIRequest::STATUS_SIZE_AVAILABLE) &&
              (status & imgIRequest::STATUS_FRAME_COMPLETE);
     }
     default:
       NS_NOTREACHED("unexpected image type");
       return false;
   }
 }
@@ -2393,20 +2376,23 @@ bool
 nsStyleImage::IsLoaded() const
 {
   switch (mType) {
     case eStyleImageType_Null:
       return false;
     case eStyleImageType_Gradient:
     case eStyleImageType_Element:
       return true;
-    case eStyleImageType_Image:
-    {
+    case eStyleImageType_Image: {
+      imgRequestProxy* req = GetImageData();
+      if (!req) {
+        return false;
+      }
       uint32_t status = imgIRequest::STATUS_ERROR;
-      return NS_SUCCEEDED(mImage->GetImageStatus(&status)) &&
+      return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
              !(status & imgIRequest::STATUS_ERROR) &&
              (status & imgIRequest::STATUS_LOAD_COMPLETE);
     }
     default:
       NS_NOTREACHED("unexpected image type");
       return false;
   }
 }
@@ -2425,17 +2411,17 @@ nsStyleImage::operator==(const nsStyleIm
     return false;
   }
 
   if (!EqualRects(mCropRect, aOther.mCropRect)) {
     return false;
   }
 
   if (mType == eStyleImageType_Image) {
-    return EqualImages(mImage, aOther.mImage);
+    return DefinitelyEqualImages(mImage, aOther.mImage);
   }
 
   if (mType == eStyleImageType_Gradient) {
     return *mGradient == *aOther.mGradient;
   }
 
   if (mType == eStyleImageType_Element) {
     return NS_strcmp(mElementId, aOther.mElementId) == 0;
@@ -2706,17 +2692,19 @@ nsStyleImageLayers::Size::DependsOnPosit
   //     according to the spec.  However, we don't implement the spec yet, so
   //     for now we bail and say element() plus auto affects ultimate size.
   if (type == eStyleImageType_Element) {
     return true;
   }
 
   if (type == eStyleImageType_Image) {
     nsCOMPtr<imgIContainer> imgContainer;
-    aImage.GetImageData()->GetImage(getter_AddRefs(imgContainer));
+    if (imgRequestProxy* req = aImage.GetImageData()) {
+      req->GetImage(getter_AddRefs(imgContainer));
+    }
     if (imgContainer) {
       CSSIntSize imageSize;
       nsSize imageRatio;
       bool hasWidth, hasHeight;
       nsLayoutUtils::ComputeSizeForDrawing(imgContainer, imageSize, imageRatio,
                                            hasWidth, hasHeight);
 
       // If the image has a fixed width and height, rendering never depends on
@@ -2940,24 +2928,30 @@ nsStyleBackground::nsStyleBackground(con
 nsStyleBackground::~nsStyleBackground()
 {
   MOZ_COUNT_DTOR(nsStyleBackground);
 }
 
 void
 nsStyleBackground::Destroy(nsPresContext* aContext)
 {
-  // Untrack all the images stored in our layers
-  mImage.UntrackImages(aContext->Document()->ImageTracker());
-
   this->~nsStyleBackground();
   aContext->PresShell()->
     FreeByObjectID(eArenaObjectID_nsStyleBackground, this);
 }
 
+void
+nsStyleBackground::FinishStyle(nsPresContext* aPresContext)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPresContext->StyleSet()->IsServo());
+
+  mImage.ResolveImages(aPresContext);
+}
+
 nsChangeHint
 nsStyleBackground::CalcDifference(const nsStyleBackground& aNewData) const
 {
   nsChangeHint hint = nsChangeHint(0);
   if (mBackgroundColor != aNewData.mBackgroundColor) {
     hint |= nsChangeHint_RepaintFrame;
   }
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -417,32 +417,38 @@ private:
 struct nsStyleImage
 {
   nsStyleImage();
   ~nsStyleImage();
   nsStyleImage(const nsStyleImage& aOther);
   nsStyleImage& operator=(const nsStyleImage& aOther);
 
   void SetNull();
-  void SetImageData(imgRequestProxy* aImage);
-  void TrackImage(mozilla::dom::ImageTracker* aImageTracker);
-  void UntrackImage(mozilla::dom::ImageTracker* aImageTracker);
+  void SetImageRequest(already_AddRefed<nsStyleImageRequest> aImage);
   void SetGradientData(nsStyleGradient* aGradient);
   void SetElementId(const char16_t* aElementId);
   void SetCropRect(mozilla::UniquePtr<nsStyleSides> aCropRect);
 
+  void ResolveImage(nsPresContext* aContext) {
+    MOZ_ASSERT(mType != eStyleImageType_Image || mImage);
+    if (mType == eStyleImageType_Image && !mImage->IsResolved()) {
+      mImage->Resolve(aContext);
+    }
+  }
+
   nsStyleImageType GetType() const {
     return mType;
   }
+  nsStyleImageRequest* GetImageRequest() const {
+    MOZ_ASSERT(mType == eStyleImageType_Image, "Data is not an image!");
+    MOZ_ASSERT(mImage);
+    return mImage;
+  }
   imgRequestProxy* GetImageData() const {
-    MOZ_ASSERT(mType == eStyleImageType_Image, "Data is not an image!");
-    MOZ_ASSERT(mImage);
-    MOZ_ASSERT(mImageTracked,
-               "Should be tracking any image we're going to use!");
-    return mImage;
+    return GetImageRequest()->get();
   }
   nsStyleGradient* GetGradientData() const {
     NS_ASSERTION(mType == eStyleImageType_Gradient, "Data is not a gradient!");
     return mGradient;
   }
   const char16_t* GetElementId() const {
     NS_ASSERTION(mType == eStyleImageType_Element, "Data is not an element!");
     return mElementId;
@@ -526,26 +532,23 @@ private:
   void EnsureCachedBIData() const;
 
   // This variable keeps some cache data for border image and is lazily
   // allocated since it is only used in border image case.
   mozilla::UniquePtr<CachedBorderImageData> mCachedBIData;
 
   nsStyleImageType mType;
   union {
-    imgRequestProxy* mImage;
+    nsStyleImageRequest* mImage;
     nsStyleGradient* mGradient;
     char16_t* mElementId;
   };
 
   // This is _currently_ used only in conjunction with eStyleImageType_Image.
   mozilla::UniquePtr<nsStyleSides> mCropRect;
-#ifdef DEBUG
-  bool mImageTracked;
-#endif
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleColor
 {
   explicit nsStyleColor(StyleStructContext aContext);
   nsStyleColor(const nsStyleColor& aOther);
   ~nsStyleColor() {
     MOZ_COUNT_DTOR(nsStyleColor);
@@ -812,26 +815,19 @@ struct nsStyleImageLayers {
     // This constructor does not initialize mRepeat or mOrigin and Initialize()
     // must be called to do that.
     Layer();
     ~Layer();
 
     // Initialize mRepeat and mOrigin by specified layer type
     void Initialize(LayerType aType);
 
-    // Register/unregister images with the document. We do this only
-    // after the dust has settled in ComputeBackgroundData.
-    void TrackImages(mozilla::dom::ImageTracker* aImageTracker) {
+    void ResolveImage(nsPresContext* aContext) {
       if (mImage.GetType() == eStyleImageType_Image) {
-        mImage.TrackImage(aImageTracker);
-      }
-    }
-    void UntrackImages(mozilla::dom::ImageTracker* aImageTracker) {
-      if (mImage.GetType() == eStyleImageType_Image) {
-        mImage.UntrackImage(aImageTracker);
+        mImage.ResolveImage(aContext);
       }
     }
 
     // True if the rendering of this layer might change when the size
     // of the background positioning area changes.  This is true for any
     // non-solid-color background whose position or size depends on
     // the size of the positioning area.  It's also true for SVG images
     // whose root <svg> node has a viewBox.
@@ -871,25 +867,21 @@ struct nsStyleImageLayers {
   // background-clip applies to the background-color) may not be last
   // layer.  In layers below the bottom layer, properties will be
   // uninitialized unless their count, above, indicates that they are
   // present.
   nsStyleAutoArray<Layer> mLayers;
 
   const Layer& BottomLayer() const { return mLayers[mImageCount - 1]; }
 
-  void TrackImages(mozilla::dom::ImageTracker* aImageTracker) {
+  void ResolveImages(nsPresContext* aContext) {
     for (uint32_t i = 0; i < mImageCount; ++i) {
-        mLayers[i].TrackImages(aImageTracker);
+      mLayers[i].ResolveImage(aContext);
     }
   }
-  void UntrackImages(mozilla::dom::ImageTracker* aImageTracker) {
-    for (uint32_t i = 0; i < mImageCount; ++i)
-      mLayers[i].UntrackImages(aImageTracker);
-  }
 
   nsChangeHint CalcDifference(const nsStyleImageLayers& aNewLayers,
                               nsStyleImageLayers::LayerType aType) const;
 
   bool HasLayerWithImage() const;
 
   static const nsCSSPropertyID kBackgroundLayerTable[];
   static const nsCSSPropertyID kMaskLayerTable[];
@@ -901,17 +893,21 @@ struct nsStyleImageLayers {
     NS_ASSERTION((count_) > 0 && (count_) <= (start_) + 1, "Invalid layer range!"); \
     for (uint32_t var_ = (start_) + 1; var_-- != (uint32_t)((start_) + 1 - (count_)); )
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBackground {
   explicit nsStyleBackground(StyleStructContext aContext);
   nsStyleBackground(const nsStyleBackground& aOther);
   ~nsStyleBackground();
-  void FinishStyle(nsPresContext* aPresContext) {}
+
+  // Resolves and tracks the images in mImage.  Only called with a Servo-backed
+  // style system, where those images must be resolved later than the OMT
+  // nsStyleBackground constructor call.
+  void FinishStyle(nsPresContext* aPresContext);
 
   void* operator new(size_t sz, nsStyleBackground* aSelf) { return aSelf; }
   void* operator new(size_t sz, nsPresContext* aContext) {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBackground, sz);
   }
   void Destroy(nsPresContext* aContext);
 
@@ -1210,17 +1206,21 @@ static bool IsVisibleBorderStyle(uint8_t
           aStyle != NS_STYLE_BORDER_STYLE_HIDDEN);
 }
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder
 {
   explicit nsStyleBorder(StyleStructContext aContext);
   nsStyleBorder(const nsStyleBorder& aBorder);
   ~nsStyleBorder();
-  void FinishStyle(nsPresContext* aPresContext) {}
+
+  // Resolves and tracks mBorderImageSource.  Only called with a Servo-backed
+  // style system, where those images must be resolved later than the OMT
+  // nsStyleBorder constructor call.
+  void FinishStyle(nsPresContext* aPresContext);
 
   void* operator new(size_t sz, nsStyleBorder* aSelf) { return aSelf; }
   void* operator new(size_t sz, nsPresContext* aContext) {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBorder, sz);
   }
   void Destroy(nsPresContext* aContext);
 
@@ -1314,26 +1314,20 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
       (HasVisibleStyle(aSide) ? mBorder.Side(aSide) : 0);
   }
 
   inline bool IsBorderImageLoaded() const
   {
     return mBorderImageSource.IsLoaded();
   }
 
-  void TrackImage(mozilla::dom::ImageTracker* aImageTracker)
+  void ResolveImage(nsPresContext* aContext)
   {
     if (mBorderImageSource.GetType() == eStyleImageType_Image) {
-      mBorderImageSource.TrackImage(aImageTracker);
-    }
-  }
-  void UntrackImage(mozilla::dom::ImageTracker* aImageTracker)
-  {
-    if (mBorderImageSource.GetType() == eStyleImageType_Image) {
-      mBorderImageSource.UntrackImage(aImageTracker);
+      mBorderImageSource.ResolveImage(aContext);
     }
   }
 
   nsMargin GetImageOutset() const;
 
   void GetCompositeColors(int32_t aIndex, nsBorderColors** aColors) const
   {
     if (!mBorderColors) {
@@ -3766,17 +3760,21 @@ struct nsTArray_CopyChooser<nsStyleFilte
   typedef nsTArray_CopyWithConstructors<nsStyleFilter> Type;
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVGReset
 {
   explicit nsStyleSVGReset(StyleStructContext aContext);
   nsStyleSVGReset(const nsStyleSVGReset& aSource);
   ~nsStyleSVGReset();
-  void FinishStyle(nsPresContext* aPresContext) {}
+
+  // Resolves and tracks the images in mMask.  Only called with a Servo-backed
+  // style system, where those images must be resolved later than the OMT
+  // nsStyleSVGReset constructor call.
+  void FinishStyle(nsPresContext* aPresContext);
 
   void* operator new(size_t sz, nsStyleSVGReset* aSelf) { return aSelf; }
   void* operator new(size_t sz, nsPresContext* aContext) {
     return aContext->PresShell()->
       AllocateByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, sz);
   }
   void Destroy(nsPresContext* aContext);