Bug 1463530: route all image requests through nsIFrame. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 22 May 2018 17:49:04 +0200
changeset 798817 c57b75f24c1dccb1fab4259e0d9f91c3166bc469
parent 798816 f43ec1498644cfd846bd486ea263cad65ada00a3
push id110846
push userbmo:emilio@crisal.io
push dateWed, 23 May 2018 14:09:28 +0000
reviewersheycam
bugs1463530
milestone62.0a1
Bug 1463530: route all image requests through nsIFrame. r?heycam For now, this makes no difference, in the sense that the general setup doesn't change and I just add two inline functions to handle all requests. In the future, this will probably go through the document's image loader, and we could just use ImageValue and such. We probably need to refactor a bit of nsImageRenderer to avoid making that a bunch of hashtable lookups, but we'll see, that's not done yet. MozReview-Commit-ID: 7vFNnVl9X7q
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsFloatManager.cpp
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsIFrame.h
layout/painting/nsCSSRendering.cpp
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/painting/nsImageRenderer.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/svg/SVGObserverUtils.cpp
layout/svg/nsSVGIntegrationUtils.cpp
layout/xul/nsImageBoxFrame.cpp
layout/xul/tree/nsTreeBodyFrame.cpp
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1702,27 +1702,29 @@ nsCSSFrameConstructor::CreateGenConTextN
     aState.mGeneratedTextNodesWithInitializer.AppendObject(content);
   }
   return content.forget();
 }
 
 already_AddRefed<nsIContent>
 nsCSSFrameConstructor::CreateGeneratedContent(nsFrameConstructorState& aState,
                                               Element* aParentContent,
+                                              nsIFrame* aParentFrame,
                                               ComputedStyle* aComputedStyle,
-                                              uint32_t        aContentIndex)
+                                              uint32_t aContentIndex)
 {
   // Get the content value
-  const nsStyleContentData &data =
+  const nsStyleContentData& data =
     aComputedStyle->StyleContent()->ContentAt(aContentIndex);
   nsStyleContentType type = data.GetType();
 
   switch (type) {
     case eStyleContentType_Image: {
-      imgRequestProxy* image = data.GetImage();
+      imgRequestProxy* image =
+        aParentFrame->GetImageRequest(*data.ImageRequest());
       if (!image) {
         // CSS had something specified that couldn't be converted to an
         // image object
         return nullptr;
       }
 
       // Create an image content object and pass it the image request.
       // XXX Check if it's an image type we can handle...
@@ -1911,18 +1913,18 @@ nsCSSFrameConstructor::CreateGeneratedCo
     // and replace old one.
     mPresShell->StyleSet()->StyleNewSubtree(container);
     pseudoComputedStyle = styleSet->ResolveServoStyle(container);
   }
 
   uint32_t contentCount = pseudoComputedStyle->StyleContent()->ContentCount();
   for (uint32_t contentIndex = 0; contentIndex < contentCount; contentIndex++) {
     nsCOMPtr<nsIContent> content =
-      CreateGeneratedContent(aState, aParentContent, pseudoComputedStyle,
-                             contentIndex);
+      CreateGeneratedContent(aState, aParentContent, aParentFrame,
+                             pseudoComputedStyle, contentIndex);
     if (content) {
       container->AppendChildTo(content, false);
       if (content->IsElement()) {
         // If we created any children elements, Servo needs to traverse them, but
         // the root is already set up.
         mPresShell->StyleSet()->StyleNewSubtree(content->AsElement());
       }
     }
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -462,16 +462,17 @@ private:
    * The caller takes care of making it SetIsNativeAnonymousRoot, binding it
    * to the document, and creating frames for it.
    * @param aParentContent is the node that has the before/after style
    * @param aComputedStyle is the 'before' or 'after' pseudo-element style.
    * @param aContentIndex is the index of the content item to create
    */
   already_AddRefed<nsIContent> CreateGeneratedContent(nsFrameConstructorState& aState,
                                                       mozilla::dom::Element* aParentContent,
+                                                      nsIFrame* aParentFrame,
                                                       ComputedStyle* aComputedStyle,
                                                       uint32_t        aContentIndex);
 
   // aFrame may be null; this method doesn't use it directly in any case.
   void CreateGeneratedContentItem(nsFrameConstructorState&   aState,
                                   nsContainerFrame*          aFrame,
                                   mozilla::dom::Element*     aContent,
                                   ComputedStyle*             aComputedStyle,
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -7097,24 +7097,24 @@ nsBlockFrame::CreateBulletFrameForListIt
 bool
 nsBlockFrame::BulletIsEmpty() const
 {
   NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay ==
                mozilla::StyleDisplay::ListItem && HasOutsideBullet(),
                "should only care when we have an outside bullet");
   const nsStyleList* list = StyleList();
   return list->mCounterStyle->IsNone() &&
-         !list->GetListStyleImage();
+         !GetImageRequest(list->mListStyleImage);
 }
 
 void
 nsBlockFrame::GetSpokenBulletText(nsAString& aText) const
 {
   const nsStyleList* myList = StyleList();
-  if (myList->GetListStyleImage()) {
+  if (GetImageRequest(myList->mListStyleImage)) {
     aText.Assign(kDiscCharacter);
     aText.Append(' ');
   } else {
     nsBulletFrame* bullet = GetBullet();
     if (bullet) {
       bullet->GetSpokenText(aText);
     } else {
       aText.Truncate();
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -101,20 +101,18 @@ nsBulletFrame::IsSelfEmpty()
   return StyleList()->mCounterStyle->IsNone();
 }
 
 /* virtual */ void
 nsBulletFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle)
 {
   nsFrame::DidSetComputedStyle(aOldComputedStyle);
 
-  imgRequestProxy *newRequest = StyleList()->GetListStyleImage();
-
+  imgRequestProxy* newRequest = GetImageRequest(StyleList()->mListStyleImage);
   if (newRequest) {
-
     if (!mListener) {
       mListener = new nsBulletListener();
       mListener->SetFrame(this);
     }
 
     bool needNewRequest = true;
 
     if (mImageRequest) {
@@ -155,21 +153,21 @@ nsBulletFrame::DidSetComputedStyle(Compu
 #ifdef ACCESSIBILITY
   // Update the list bullet accessible. If old style list isn't available then
   // no need to update the accessible tree because it's not created yet.
   if (aOldComputedStyle) {
     nsAccessibilityService* accService = nsIPresShell::AccService();
     if (accService) {
       const nsStyleList* oldStyleList = aOldComputedStyle->PeekStyleList();
       if (oldStyleList) {
-        bool hadBullet = oldStyleList->GetListStyleImage() ||
+        bool hadBullet = GetImageRequest(oldStyleList->mListStyleImage) ||
           !oldStyleList->mCounterStyle->IsNone();
 
         const nsStyleList* newStyleList = StyleList();
-        bool hasBullet = newStyleList->GetListStyleImage() ||
+        bool hasBullet = GetImageRequest(newStyleList->mListStyleImage) ||
           !newStyleList->mCounterStyle->IsNone();
 
         if (hadBullet != hasBullet) {
           accService->UpdateListBullet(PresContext()->GetPresShell(), mContent,
                                        hasBullet);
         }
       }
     }
@@ -736,17 +734,17 @@ nsBulletFrame::BuildDisplayList(nsDispla
 
 Maybe<BulletRenderer>
 nsBulletFrame::CreateBulletRenderer(gfxContext& aRenderingContext, nsPoint aPt)
 {
   const nsStyleList* myList = StyleList();
   CounterStyle* listStyleType = myList->mCounterStyle;
   nsMargin padding = mPadding.GetPhysicalMargin(GetWritingMode());
 
-  if (myList->GetListStyleImage() && mImageRequest) {
+  if (GetImageRequest(myList->mListStyleImage) && mImageRequest) {
     uint32_t status;
     mImageRequest->GetImageStatus(&status);
     if (status & imgIRequest::STATUS_LOAD_COMPLETE &&
         !(status & imgIRequest::STATUS_ERROR)) {
       nsCOMPtr<imgIContainer> imageCon;
       mImageRequest->GetImage(getter_AddRefs(imageCon));
       if (imageCon) {
         nsRect dest(padding.left, padding.top,
@@ -986,17 +984,20 @@ nsBulletFrame::GetDesiredSize(nsPresCont
 
   const nsStyleList* myList = StyleList();
   nscoord ascent;
   RefPtr<nsFontMetrics> fm =
     nsLayoutUtils::GetFontMetricsForFrame(this, aFontSizeInflation);
 
   RemoveStateBits(BULLET_FRAME_IMAGE_LOADING);
 
-  if (myList->GetListStyleImage() && mImageRequest) {
+  // FIXME(emilio): Moderately sure the GetImageRequest checks here are not
+  // needed and are an artifact from bug 1310463. Checking mImageRequest should
+  // just work here.
+  if (GetImageRequest(myList->mListStyleImage) && mImageRequest) {
     uint32_t status;
     mImageRequest->GetImageStatus(&status);
     if (status & imgIRequest::STATUS_SIZE_AVAILABLE &&
         !(status & imgIRequest::STATUS_ERROR)) {
       // auto size the image
       finalSize.ISize(wm) = mIntrinsicSize.ISize(wm);
       aMetrics.SetBlockStartAscent(finalSize.BSize(wm) =
                                    mIntrinsicSize.BSize(wm));
@@ -1139,17 +1140,17 @@ nsBulletFrame::GetPrefISize(gfxContext *
 static inline bool
 IsIgnoreable(const nsIFrame* aFrame, nscoord aISize)
 {
   if (aISize != nscoord(0)) {
     return false;
   }
   auto listStyle = aFrame->StyleList();
   return listStyle->mCounterStyle->IsNone() &&
-         !listStyle->GetListStyleImage();
+         !aFrame->GetImageRequest(listStyle->mListStyleImage);
 }
 
 /* virtual */ void
 nsBulletFrame::AddInlineMinISize(gfxContext* aRenderingContext,
                                  nsIFrame::InlineMinISizeData* aData)
 {
   nscoord isize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                     this, nsLayoutUtils::MIN_ISIZE);
@@ -1321,17 +1322,17 @@ nsBulletFrame::SetFontSizeInflation(floa
 
   AddStateBits(BULLET_FRAME_HAS_FONT_INFLATION);
   SetProperty(FontSizeInflationProperty(), aInflation);
 }
 
 already_AddRefed<imgIContainer>
 nsBulletFrame::GetImage() const
 {
-  if (mImageRequest && StyleList()->GetListStyleImage()) {
+  if (mImageRequest && GetImageRequest(StyleList()->mListStyleImage)) {
     nsCOMPtr<imgIContainer> imageCon;
     mImageRequest->GetImage(getter_AddRefs(imageCon));
     return imageCon.forget();
   }
 
   return nullptr;
 }
 
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -588,17 +588,17 @@ public:
     nscoord aShapeMargin,
     nsIFrame* const aFrame,
     const LogicalRect& aShapeBoxRect,
     const LogicalRect& aMarginRect,
     WritingMode aWM,
     const nsSize& aContainerSize);
 
   static UniquePtr<ShapeInfo> CreateImageShape(
-    const UniquePtr<nsStyleImage>& aShapeImage,
+    nsStyleImage& aShapeImage,
     float aShapeImageThreshold,
     nscoord aShapeMargin,
     nsIFrame* const aFrame,
     const LogicalRect& aMarginRect,
     WritingMode aWM,
     const nsSize& aContainerSize);
 
 protected:
@@ -2366,17 +2366,17 @@ nsFloatManager::FloatInfo::FloatInfo(nsI
       return;
 
     case StyleShapeSourceType::URL:
       MOZ_ASSERT_UNREACHABLE("shape-outside doesn't have URL source type!");
       return;
 
     case StyleShapeSourceType::Image: {
       float shapeImageThreshold = mFrame->StyleDisplay()->mShapeImageThreshold;
-      mShapeInfo = ShapeInfo::CreateImageShape(shapeOutside.GetShapeImage(),
+      mShapeInfo = ShapeInfo::CreateImageShape(shapeOutside.ShapeImage(),
                                                shapeImageThreshold,
                                                shapeMargin,
                                                mFrame,
                                                aMarginRect,
                                                aWM,
                                                aContainerSize);
       if (!mShapeInfo) {
         // Image is not ready, or fails to load, etc.
@@ -2767,29 +2767,29 @@ nsFloatManager::ShapeInfo::CreatePolygon
   // computes the float area using a rasterization method.
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   return MakeUnique<PolygonShapeInfo>(Move(vertices), aShapeMargin,
                                       appUnitsPerDevPixel, marginRect);
 }
 
 /* static */ UniquePtr<nsFloatManager::ShapeInfo>
 nsFloatManager::ShapeInfo::CreateImageShape(
-  const UniquePtr<nsStyleImage>& aShapeImage,
+  nsStyleImage& aShapeImage,
   float aShapeImageThreshold,
   nscoord aShapeMargin,
   nsIFrame* const aFrame,
   const LogicalRect& aMarginRect,
   WritingMode aWM,
   const nsSize& aContainerSize)
 {
-  MOZ_ASSERT(aShapeImage ==
-             aFrame->StyleDisplay()->mShapeOutside.GetShapeImage(),
+  MOZ_ASSERT(&aShapeImage ==
+             &aFrame->StyleDisplay()->mShapeOutside.ShapeImage(),
              "aFrame should be the frame that we got aShapeImage from");
 
-  nsImageRenderer imageRenderer(aFrame, aShapeImage.get(),
+  nsImageRenderer imageRenderer(aFrame, &aShapeImage,
                                 nsImageRenderer::FLAG_SYNC_DECODE_IMAGES);
 
   if (!imageRenderer.PrepareImage()) {
     // The image is not ready yet.
     return nullptr;
   }
 
   nsRect contentRect = aFrame->GetContentRect();
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -840,34 +840,41 @@ nsresult
 nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const
 {
   aStart = 0;
   aEnd = 0;
   return NS_OK;
 }
 
 static void
-CompareLayers(const nsStyleImageLayers* aFirstLayers,
+CompareLayers(nsIFrame* aFrame,
+              const nsStyleImageLayers* aFirstLayers,
               const nsStyleImageLayers* aSecondLayers,
               const std::function<void(imgRequestProxy* aReq)>& aCallback)
 {
   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers)) {
     const nsStyleImage& image = aFirstLayers->mLayers[i].mImage;
+    // TODO(emilio): Pretty sure the image is always resolved already by the
+    // time we get here.
     if (image.GetType() != eStyleImageType_Image || !image.IsResolved()) {
       continue;
     }
 
     // aCallback is called when the style image in aFirstLayers is thought to
     // be different with the corresponded one in aSecondLayers
+    imgRequestProxy* thisRequest =
+      aFrame->GetImageRequest(*image.ImageRequest());
+    if (!thisRequest) {
+      continue;
+    }
+
     if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
-        (!aSecondLayers->mLayers[i].mImage.IsResolved() ||
-         !image.ImageDataEquals(aSecondLayers->mLayers[i].mImage))) {
-      if (imgRequestProxy* req = image.GetImageData()) {
-        aCallback(req);
-      }
+        !aSecondLayers->mLayers[i].mImage.IsResolved() ||
+        thisRequest != aFrame->GetImageRequest(aSecondLayers->mLayers[i].mImage)) {
+      aCallback(thisRequest);
     }
   }
 }
 
 static void
 AddAndRemoveImageAssociations(nsFrame* aFrame,
                               const nsStyleImageLayers* aOldLayers,
                               const nsStyleImageLayers* aNewLayers)
@@ -879,23 +886,23 @@ AddAndRemoveImageAssociations(nsFrame* a
   // and new context does not have the same image, clear the image load
   // notifier (which keeps the image loading, if it still is) for the frame.
   // We want to do this conservatively because some frames paint their
   // backgrounds from some other frame's style data, and we don't want
   // to clear those notifiers unless we have to.  (They'll be reset
   // when we paint, although we could miss a notification in that
   // interval.)
   if (aOldLayers && aFrame->HasImageRequest()) {
-    CompareLayers(aOldLayers, aNewLayers,
+    CompareLayers(aFrame, aOldLayers, aNewLayers,
       [&imageLoader, aFrame](imgRequestProxy* aReq)
       { imageLoader->DisassociateRequestFromFrame(aReq, aFrame); }
     );
   }
 
-  CompareLayers(aNewLayers, aOldLayers,
+  CompareLayers(aFrame, aNewLayers, aOldLayers,
     [&imageLoader, aFrame](imgRequestProxy* aReq)
     { imageLoader->AssociateRequestToFrame(aReq, aFrame, 0); }
   );
 }
 
 void
 nsIFrame::AddDisplayItem(nsDisplayItem* aItem)
 {
@@ -1131,20 +1138,21 @@ nsFrame::DidSetComputedStyle(ComputedSty
       if (oldValue != newValue &&
           !HasProperty(UsedBorderProperty())) {
         AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
       }
     }
   }
 
   ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
-  imgIRequest *oldBorderImage = aOldComputedStyle
-    ? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
+  imgRequestProxy* oldBorderImage = aOldComputedStyle
+    ? GetImageRequest(aOldComputedStyle->StyleBorder()->mBorderImageSource)
     : nullptr;
-  imgIRequest *newBorderImage = StyleBorder()->GetBorderImageRequest();
+  imgRequestProxy* newBorderImage =
+    GetImageRequest(StyleBorder()->mBorderImageSource);
   // FIXME (Bug 759996): The following is no longer true.
   // For border-images, we can't be as conservative (we need to set the
   // new loaders if there has been any change) since the CalcDifference
   // call depended on the result of GetComputedBorder() and that result
   // depends on whether the image has loaded, start the image load now
   // so that we'll get notified when it completes loading and can do a
   // restyle.  Otherwise, the image might finish loading from the
   // network before we start listening to its notifications, and then
@@ -1160,21 +1168,22 @@ nsFrame::DidSetComputedStyle(ComputedSty
       imageLoader->DisassociateRequestFromFrame(oldBorderImage, this);
     }
     if (newBorderImage) {
       imageLoader->AssociateRequestToFrame(newBorderImage, this, 0);
     }
   }
 
   imgIRequest* oldShapeImage =
-      aOldComputedStyle
-    ? aOldComputedStyle->StyleDisplay()->mShapeOutside.GetShapeImageData()
-    : nullptr;
+    aOldComputedStyle
+      ? GetImageRequest(aOldComputedStyle->StyleDisplay()->mShapeOutside.GetShapeImage())
+      : nullptr;
+
   imgIRequest* newShapeImage =
-    StyleDisplay()->mShapeOutside.GetShapeImageData();
+    GetImageRequest(StyleDisplay()->mShapeOutside.GetShapeImage());
 
   if (oldShapeImage != newShapeImage) {
     if (oldShapeImage && HasImageRequest()) {
       imageLoader->DisassociateRequestFromFrame(oldShapeImage, this);
     }
     if (newShapeImage) {
       imageLoader->AssociateRequestToFrame(newShapeImage, this,
         ImageLoader::REQUEST_REQUIRES_REFLOW);
@@ -5218,34 +5227,29 @@ nsIFrame::ContentOffsets nsFrame::CalcCo
 {
   return OffsetsForSingleFrame(this, aPoint);
 }
 
 void
 nsIFrame::AssociateImage(const nsStyleImage& aImage, nsPresContext* aPresContext,
                          uint32_t aImageLoaderFlags)
 {
-  if (aImage.GetType() != eStyleImageType_Image) {
-    return;
-  }
-
-  imgRequestProxy* req = aImage.GetImageData();
+  imgRequestProxy* req = GetImageRequest(aImage);
   if (!req) {
     return;
   }
   mozilla::css::ImageLoader* loader =
     aPresContext->Document()->StyleImageLoader();
 
   // If this fails there's not much we can do ...
   loader->AssociateRequestToFrame(req, this, aImageLoaderFlags);
 }
 
 nsresult
-nsFrame::GetCursor(const nsPoint& aPoint,
-                   nsIFrame::Cursor& aCursor)
+nsFrame::GetCursor(const nsPoint& aPoint, nsIFrame::Cursor& aCursor)
 {
   FillCursorInformationFromStyle(StyleUserInterface(), aCursor);
   if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
     // If this is editable, I-beam cursor is better for most elements.
     aCursor.mCursor =
       (mContent && mContent->IsEditable())
       ? NS_STYLE_CURSOR_TEXT : NS_STYLE_CURSOR_DEFAULT;
   }
@@ -10113,17 +10117,17 @@ void nsFrame::FillCursorInformationFromS
 {
   aCursor.mCursor = ui->mCursor;
   aCursor.mHaveHotspot = false;
   aCursor.mLoading = false;
   aCursor.mHotspotX = aCursor.mHotspotY = 0.0f;
 
   for (const nsCursorImage& item : ui->mCursorImages) {
     uint32_t status;
-    imgRequestProxy* req = item.GetImage();
+    imgRequestProxy* req = GetImageRequest(item.mImage);
     if (!req || NS_FAILED(req->GetImageStatus(&status))) {
       continue;
     }
     if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
       // If we are falling back because any cursor before is loading,
       // let the consumer know.
       aCursor.mLoading = true;
     } else if (!(status & imgIRequest::STATUS_ERROR)) {
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -674,18 +674,21 @@ protected:
   nsresult GetDataForTableSelection(const nsFrameSelection* aFrameSelection,
                                     nsIPresShell* aPresShell,
                                     mozilla::WidgetMouseEvent* aMouseEvent,
                                     nsIContent** aParentContent,
                                     int32_t* aContentOffset,
                                     mozilla::TableSelection* aTarget);
 
   // Fills aCursor with the appropriate information from ui
-  static void FillCursorInformationFromStyle(const nsStyleUserInterface* ui,
-                                             nsIFrame::Cursor& aCursor);
+  //
+  // NOTE: the nsStyleUserInterface could be from a pseudo style instead of our
+  // style.
+  void FillCursorInformationFromStyle(const nsStyleUserInterface* aUi,
+                                      nsIFrame::Cursor& aCursor);
   NS_IMETHOD DoXULLayout(nsBoxLayoutState& aBoxLayoutState) override;
 
   nsBoxLayoutMetrics* BoxMetrics() const;
 
   // Fire DOM event. If no aContent argument use frame's mContent.
   void FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent = nullptr);
 
 private:
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -63,16 +63,17 @@
  * 4. if you move a frame (outside of the reflow process, or after reflowing it),
  *    then you must make sure that its view (or its child frame's views) are re-positioned
  *    as well. It's reasonable to not position the view until after all reflowing the
  *    entire line, for example, but the frame should still be positioned and sized (and
  *    the view sized) during the reflow (i.e., before sending the DidReflow() notification)
  * 5. the view system handles moving of widgets, i.e., it's not our problem
  */
 
+class imgRequestProxy;
 class nsAtom;
 class nsPresContext;
 class nsIPresShell;
 class nsView;
 class nsIWidget;
 class nsISelectionController;
 class nsBoxLayoutState;
 class nsBoxLayout;
@@ -4095,16 +4096,47 @@ public:
 
   // Returns true if this frame is visible or may have visible descendants.
   bool IsVisibleOrMayHaveVisibleDescendants() const {
     return !mAllDescendantsAreInvisible || StyleVisibility()->IsVisible();
   }
   // Update mAllDescendantsAreInvisible flag for this frame and ancestors.
   void UpdateVisibleDescendantsState();
 
+  // Gets an image request for a given nsStyleImage, or null if the image is not
+  // well, an image, but other thing like, e.g., a gradient.
+  imgRequestProxy* GetImageRequest(const nsStyleImage& aImage) const
+  {
+    if (aImage.GetType() != eStyleImageType_Image) {
+      return nullptr;
+    }
+
+    return GetImageRequest(*aImage.ImageRequest());
+  }
+
+  imgRequestProxy* GetImageRequest(const nsStyleImage* aImage) const
+  {
+    return aImage ? GetImageRequest(*aImage) : nullptr;
+  }
+
+  // Gets an image request for a given nsStyleImageRequest.
+  //
+  // NOTE(emilio): This is a temporary state until we move resolved image
+  // requests outside of the style system.
+  imgRequestProxy* GetImageRequest(nsStyleImageRequest& aRequest) const
+  {
+    return aRequest.get();
+  }
+
+  // Gets an image request for a given nsStyleImageRequest.
+  imgRequestProxy* GetImageRequest(nsStyleImageRequest* aRequest) const
+  {
+    return aRequest ? GetImageRequest(*aRequest) : nullptr;
+  }
+
   /**
    * If this returns true, the frame it's called on should get the
    * NS_FRAME_HAS_DIRTY_CHILDREN bit set on it by the caller; either directly
    * if it's already in reflow, or via calling FrameNeedsReflow() to schedule a
    * reflow.
    */
   virtual bool RenumberFrameAndDescendants(int32_t* aOrdinal,
                                            int32_t aDepth,
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -2074,17 +2074,18 @@ nsCSSRendering::CanBuildWebRenderDisplay
 
   // We only support painting gradients and image for a single style image layer
   const nsStyleImage* styleImage = &aBackgroundStyle->mImage.mLayers[aLayer].mImage;
   if (styleImage->GetType() == eStyleImageType_Image) {
     if (styleImage->GetCropRect()) {
       return false;
     }
 
-    imgRequestProxy* requestProxy = styleImage->GetImageData();
+    imgRequestProxy* requestProxy =
+      aFrame->GetImageRequest(*styleImage->ImageRequest());
     if (!requestProxy) {
       return false;
     }
 
     uint32_t imageFlags = imgIContainer::FLAG_NONE;
     if (aPaintFlags & nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES) {
       imageFlags |= imgIContainer::FLAG_SYNC_DECODE;
     }
@@ -2575,17 +2576,17 @@ nsCSSRendering::DetermineBackgroundColor
   // We can skip painting the background color if a background image is opaque.
   nsStyleImageLayers::Repeat repeat = bg->BottomLayer().mRepeat;
   bool xFullRepeat = repeat.mXRepeat == StyleImageLayerRepeat::Repeat ||
                      repeat.mXRepeat == StyleImageLayerRepeat::Round;
   bool yFullRepeat = repeat.mYRepeat == StyleImageLayerRepeat::Repeat ||
                      repeat.mYRepeat == StyleImageLayerRepeat::Round;
   if (aDrawBackgroundColor &&
       xFullRepeat && yFullRepeat &&
-      bg->BottomLayer().mImage.IsOpaque() &&
+      bg->BottomLayer().mImage.IsOpaque(aFrame) &&
       bg->BottomLayer().mBlendMode == NS_STYLE_BLEND_NORMAL) {
     aDrawBackgroundColor = false;
   }
 
   return bgColor;
 }
 
 static CompositionOp
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4015,17 +4015,18 @@ nsDisplayBackgroundImage::ShouldCreateOw
                                                        backgroundStyleFrame)) {
     return WHENEVER_POSSIBLE;
   }
 
   if (nsLayoutUtils::AnimatedImageLayersEnabled() && mBackgroundStyle) {
     const nsStyleImageLayers::Layer &layer = mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
     const nsStyleImage* image = &layer.mImage;
     if (image->GetType() == eStyleImageType_Image) {
-      imgIRequest* imgreq = image->GetImageData();
+      imgRequestProxy* imgreq =
+        backgroundStyleFrame->GetImageRequest(*image->ImageRequest());
       nsCOMPtr<imgIContainer> image;
       if (imgreq &&
           NS_SUCCEEDED(imgreq->GetImage(getter_AddRefs(image))) &&
           image) {
         bool animated = false;
         if (NS_SUCCEEDED(image->GetAnimated(&animated)) && animated) {
           return WHENEVER_POSSIBLE;
         }
@@ -4234,17 +4235,18 @@ nsDisplayBackgroundImage::GetOpaqueRegio
   // this could easily lead to O(N^2) behavior inside InlineBackgroundData,
   // which expects frames to be sent to it in content order, not reverse
   // content order which we'll produce here.
   // Of course, if there's only one frame in the flow, it doesn't matter.
   if (mFrame->StyleBorder()->mBoxDecorationBreak ==
         StyleBoxDecorationBreak::Clone ||
       (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) {
     const nsStyleImageLayers::Layer& layer = mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
-    if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL &&
+    if (layer.mImage.IsOpaque(mFrame) &&
+        layer.mBlendMode == NS_STYLE_BLEND_NORMAL &&
         layer.mRepeat.mXRepeat != StyleImageLayerRepeat::Space &&
         layer.mRepeat.mYRepeat != StyleImageLayerRepeat::Space &&
         layer.mClip != StyleGeometryBox::Text) {
       result = GetInsideClipRegion(this, layer.mClip, mBounds, mBackgroundRect);
     }
   }
 
   return result;
@@ -4284,17 +4286,17 @@ nsDisplayBackgroundImage::RenderingMight
   nscoord radii[8];
   if (mFrame->GetBorderRadii(radii)) {
     // A change in the size of the positioning area might change the position
     // of the rounded corners.
     return true;
   }
 
   const nsStyleImageLayers::Layer &layer = mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
-  if (layer.RenderingMightDependOnPositioningAreaSizeChange()) {
+  if (layer.RenderingMightDependOnPositioningAreaSizeChange(mFrame)) {
     return true;
   }
   return false;
 }
 
 void
 nsDisplayBackgroundImage::Paint(nsDisplayListBuilder* aBuilder,
                                 gfxContext* aCtx) {
@@ -5426,17 +5428,17 @@ nsDisplayBorder::nsDisplayBorder(nsDispl
 
 bool
 nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect) const
 {
   nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() +
     ToReferenceFrame();
   const nsStyleBorder *styleBorder;
   if (paddingRect.Contains(aRect) &&
-      !(styleBorder = mFrame->StyleBorder())->IsBorderImageLoaded() &&
+      !(styleBorder = mFrame->StyleBorder())->IsBorderImageLoaded(mFrame) &&
       !nsLayoutUtils::HasNonZeroCorner(styleBorder->mBorderRadius)) {
     // aRect is entirely inside the content rect, and no part
     // of the border is rendered inside the content rect, so we are not
     // visible
     // Skip this if there's a border-image (which draws a background
     // too) or if there is a border-radius (which makes the border draw
     // further in).
     return true;
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -3786,17 +3786,17 @@ protected:
                                           mozilla::wr::IpcResourceUpdateQueue& aResource,
                                           const StackingContextHelper& aSc,
                                           mozilla::layers::WebRenderLayerManager* aManager,
                                           nsDisplayListBuilder* aDisplayListBuilder);
   template<typename T>
   T CalculateBounds(const nsStyleBorder& aStyleBorder) const
   {
     nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize());
-    if (aStyleBorder.IsBorderImageLoaded()) {
+    if (aStyleBorder.IsBorderImageLoaded(mFrame)) {
       borderBounds.Inflate(aStyleBorder.GetImageOutset());
       return borderBounds;
     } else {
       nsMargin border = aStyleBorder.GetComputedBorder();
       T result;
       if (border.top > 0) {
         result = nsRect(borderBounds.X(), borderBounds.Y(), borderBounds.Width(), border.top);
       }
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -65,47 +65,39 @@ nsImageRenderer::nsImageRenderer(nsIFram
   , mPrepareResult(ImgDrawResult::NOT_READY)
   , mSize(0, 0)
   , mFlags(aFlags)
   , mExtendMode(ExtendMode::CLAMP)
   , mMaskOp(NS_STYLE_MASK_MODE_MATCH_SOURCE)
 {
 }
 
-nsImageRenderer::~nsImageRenderer()
-{
-}
+nsImageRenderer::~nsImageRenderer() = default;
 
 static bool
-ShouldTreatAsCompleteDueToSyncDecode(const nsStyleImage* aImage,
-                                     uint32_t aFlags)
+ShouldTreatAsCompleteDueToSyncDecode(imgRequestProxy* aRequest, uint32_t aFlags)
 {
   if (!(aFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES)) {
     return false;
   }
 
-  if (aImage->GetType() != eStyleImageType_Image) {
-    return false;
-  }
-
-  imgRequestProxy* req = aImage->GetImageData();
-  if (!req) {
+  if (!aRequest) {
     return false;
   }
 
   uint32_t status = 0;
-  if (NS_FAILED(req->GetImageStatus(&status))) {
+  if (NS_FAILED(aRequest->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;
-    req->GetImage(getter_AddRefs(image));
+    aRequest->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;
   }
@@ -116,49 +108,50 @@ ShouldTreatAsCompleteDueToSyncDecode(con
 bool
 nsImageRenderer::PrepareImage()
 {
   if (mImage->IsEmpty()) {
     mPrepareResult = ImgDrawResult::BAD_IMAGE;
     return false;
   }
 
-  if (!mImage->IsComplete()) {
+  imgRequestProxy* request = mForFrame->GetImageRequest(*mImage);
+  if (!mImage->IsComplete(mForFrame)) {
     // Make sure the image is actually decoding.
-    bool frameComplete = mImage->StartDecoding();
+    bool frameComplete = mImage->StartDecoding(mForFrame);
 
     // Check again to see if we finished.
     // We cannot prepare the image for rendering if it is not fully loaded.
     // Special case: If we requested a sync decode and the image has loaded, push
     // on through because the Draw() will do a sync decode then.
-    if (!(frameComplete || mImage->IsComplete()) &&
-        !ShouldTreatAsCompleteDueToSyncDecode(mImage, mFlags)) {
+    if (!(frameComplete || mImage->IsComplete(mForFrame)) &&
+        !ShouldTreatAsCompleteDueToSyncDecode(request, mFlags)) {
       mPrepareResult = ImgDrawResult::NOT_READY;
       return false;
     }
   }
 
   switch (mType) {
     case eStyleImageType_Image: {
-      MOZ_ASSERT(mImage->GetImageData(),
+      MOZ_ASSERT(request,
                  "must have image data, since we checked IsEmpty above");
       nsCOMPtr<imgIContainer> srcImage;
       DebugOnly<nsresult> rv =
-        mImage->GetImageData()->GetImage(getter_AddRefs(srcImage));
+        request->GetImage(getter_AddRefs(srcImage));
       MOZ_ASSERT(NS_SUCCEEDED(rv) && srcImage,
                  "If GetImage() is failing, mImage->IsComplete() "
                  "should have returned false");
 
       if (!mImage->GetCropRect()) {
         mImageContainer.swap(srcImage);
       } else {
         nsIntRect actualCropRect;
         bool isEntireImage;
         bool success =
-          mImage->ComputeActualCropRect(actualCropRect, &isEntireImage);
+          mImage->ComputeActualCropRect(mForFrame, actualCropRect, &isEntireImage);
         if (!success || actualCropRect.IsEmpty()) {
           // The cropped image has zero size
           mPrepareResult = ImgDrawResult::BAD_IMAGE;
           return false;
         }
         if (isEntireImage) {
           // The cropped image is identical to the source image
           mImageContainer.swap(srcImage);
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1346,20 +1346,17 @@ nsComputedDOMStyle::DoGetContent()
       case eStyleContentType_String: {
         nsAutoString str;
         nsStyleUtil::AppendEscapedCSSString(
           nsDependentString(data.GetString()), str);
         val->SetString(str);
         break;
       }
       case eStyleContentType_Image: {
-        nsCOMPtr<nsIURI> uri;
-        if (imgRequestProxy* image = data.GetImage()) {
-          image->GetURI(getter_AddRefs(uri));
-        }
+        nsCOMPtr<nsIURI> uri = data.ImageRequest()->GetImageURI();
         val->SetURI(uri);
         break;
       }
       case eStyleContentType_Attr: {
         // XXXbholley: We don't correctly serialize namespaces here. Doing so
         // would require either storing the prefix on the nsStyleContentAttr,
         // or poking at the namespaces in the stylesheet to map from the
         // namespace URL.
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1050,28 +1050,16 @@ StyleShapeSource::SetURL(css::URLValue* 
 void
 StyleShapeSource::SetShapeImage(UniquePtr<nsStyleImage> aShapeImage)
 {
   MOZ_ASSERT(aShapeImage);
   mShapeImage = Move(aShapeImage);
   mType = StyleShapeSourceType::Image;
 }
 
-imgIRequest*
-StyleShapeSource::GetShapeImageData() const
-{
-  if (mType != StyleShapeSourceType::Image) {
-    return nullptr;
-  }
-  if (mShapeImage->GetType() != eStyleImageType_Image) {
-    return nullptr;
-  }
-  return mShapeImage->GetImageData();
-}
-
 void
 StyleShapeSource::SetBasicShape(UniquePtr<StyleBasicShape> aBasicShape,
                                 StyleGeometryBox aReferenceBox)
 {
   MOZ_ASSERT(aBasicShape);
   mBasicShape = Move(aBasicShape);
   mReferenceBox = aReferenceBox;
   mType = StyleShapeSourceType::Shape;
@@ -2464,24 +2452,25 @@ nsStyleImageRequest::GetImageURI() const
     return nullptr;
   }
 
   uri = mImageValue->GetURI();
   return uri.forget();
 }
 
 bool
-nsStyleImage::ComputeActualCropRect(nsIntRect& aActualCropRect,
+nsStyleImage::ComputeActualCropRect(nsIFrame* aForFrame,
+                                    nsIntRect& aActualCropRect,
                                     bool* aIsEntireImage) const
 {
   MOZ_ASSERT(mType == eStyleImageType_Image,
              "This function is designed to be used only when mType"
              "is eStyleImageType_Image.");
 
-  imgRequestProxy* req = GetImageData();
+  imgRequestProxy* req = aForFrame->GetImageRequest(*ImageRequest());
   if (!req) {
     return false;
   }
 
   nsCOMPtr<imgIContainer> imageContainer;
   req->GetImage(getter_AddRefs(imageContainer));
   if (!imageContainer) {
     return false;
@@ -2506,107 +2495,109 @@ nsStyleImage::ComputeActualCropRect(nsIn
 
   if (aIsEntireImage) {
     *aIsEntireImage = aActualCropRect.IsEqualInterior(imageRect);
   }
   return true;
 }
 
 bool
-nsStyleImage::StartDecoding() const
+nsStyleImage::StartDecoding(nsIFrame* aForFrame) const
 {
   if (mType == eStyleImageType_Image) {
-    imgRequestProxy* req = GetImageData();
+    imgRequestProxy* req = aForFrame->GetImageRequest(*ImageRequest());
     if (!req) {
       return false;
     }
     return req->StartDecodingWithResult(imgIContainer::FLAG_ASYNC_NOTIFY);
   }
   // null image types always return false from IsComplete, so we do the same here.
-  return mType != eStyleImageType_Null ? true : false;
+  return mType != eStyleImageType_Null;
 }
 
 bool
-nsStyleImage::IsOpaque() const
-{
-  if (!IsComplete()) {
+nsStyleImage::IsOpaque(nsIFrame* aForFrame) const
+{
+  if (!IsComplete(aForFrame)) {
     return false;
   }
 
   if (mType == eStyleImageType_Gradient) {
     return mGradient->IsOpaque();
   }
 
   if (mType == eStyleImageType_Element || mType == eStyleImageType_URL) {
     return false;
   }
 
   MOZ_ASSERT(mType == eStyleImageType_Image, "unexpected image type");
-  MOZ_ASSERT(GetImageData(), "should've returned earlier above");
+  imgRequestProxy* req = aForFrame->GetImageRequest(*ImageRequest());
+  MOZ_ASSERT(req, "should've returned earlier above");
 
   nsCOMPtr<imgIContainer> imageContainer;
-  GetImageData()->GetImage(getter_AddRefs(imageContainer));
+  req->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;
     }
 
     // Must make sure if mCropRect contains at least a pixel.
     // XXX Is this optimization worth it? Maybe I should just return false.
     nsIntRect actualCropRect;
-    return ComputeActualCropRect(actualCropRect) && !actualCropRect.IsEmpty();
+    return ComputeActualCropRect(aForFrame, actualCropRect) &&
+      !actualCropRect.IsEmpty();
   }
 
   return false;
 }
 
 bool
-nsStyleImage::IsComplete() const
+nsStyleImage::IsComplete(nsIFrame* aForFrame) const
 {
   switch (mType) {
     case eStyleImageType_Null:
       return false;
     case eStyleImageType_Gradient:
     case eStyleImageType_Element:
     case eStyleImageType_URL:
       return true;
     case eStyleImageType_Image: {
       if (!IsResolved()) {
         return false;
       }
-      imgRequestProxy* req = GetImageData();
+      imgRequestProxy* req = aForFrame->GetImageRequest(*ImageRequest());
       if (!req) {
         return false;
       }
       uint32_t status = imgIRequest::STATUS_ERROR;
       return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
              (status & imgIRequest::STATUS_SIZE_AVAILABLE) &&
              (status & imgIRequest::STATUS_FRAME_COMPLETE);
     }
     default:
       NS_NOTREACHED("unexpected image type");
       return false;
   }
 }
 
 bool
-nsStyleImage::IsLoaded() const
+nsStyleImage::IsLoaded(nsIFrame* aForFrame) const
 {
   switch (mType) {
     case eStyleImageType_Null:
       return false;
     case eStyleImageType_Gradient:
     case eStyleImageType_Element:
     case eStyleImageType_URL:
       return true;
     case eStyleImageType_Image: {
-      imgRequestProxy* req = GetImageData();
+      imgRequestProxy* req = aForFrame->GetImageRequest(*ImageRequest());
       if (!req) {
         return false;
       }
       uint32_t status = imgIRequest::STATUS_ERROR;
       return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
              !(status & imgIRequest::STATUS_ERROR) &&
              (status & imgIRequest::STATUS_LOAD_COMPLETE);
     }
@@ -2981,17 +2972,19 @@ Position::SetInitialZeroValues()
   mXPosition.mLength = 0;
   mXPosition.mHasPercent = false;
   mYPosition.mPercent = 0;
   mYPosition.mLength = 0;
   mYPosition.mHasPercent = false;
 }
 
 bool
-nsStyleImageLayers::Size::DependsOnPositioningAreaSize(const nsStyleImage& aImage) const
+nsStyleImageLayers::Size::DependsOnPositioningAreaSize(
+  nsIFrame* aFrame,
+  const nsStyleImage& aImage) const
 {
   MOZ_ASSERT(aImage.GetType() != eStyleImageType_Null,
              "caller should have handled this");
 
   // If either dimension contains a non-zero percentage, rendering for that
   // dimension straightforwardly depends on frame size.
   if ((mWidthType == eLengthPercentage && mWidth.mPercent != 0.0f) ||
       (mHeightType == eLengthPercentage && mHeight.mPercent != 0.0f)) {
@@ -3026,17 +3019,17 @@ 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;
-    if (imgRequestProxy* req = aImage.GetImageData()) {
+    if (imgRequestProxy* req = aFrame->GetImageRequest(*aImage.ImageRequest())) {
       req->GetImage(getter_AddRefs(imgContainer));
     }
     if (imgContainer) {
       CSSIntSize imageSize;
       nsSize imageRatio;
       bool hasWidth, hasHeight;
       nsLayoutUtils::ComputeSizeForDrawing(imgContainer, imageSize, imageRatio,
                                            hasWidth, hasHeight);
@@ -3116,25 +3109,26 @@ nsStyleImageLayers::Layer::Initialize(ns
     mOrigin = StyleGeometryBox::PaddingBox;
   } else {
     MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type.");
     mOrigin = StyleGeometryBox::BorderBox;
   }
 }
 
 bool
-nsStyleImageLayers::Layer::RenderingMightDependOnPositioningAreaSizeChange() const
+nsStyleImageLayers::Layer::RenderingMightDependOnPositioningAreaSizeChange(
+    nsIFrame* aFrame) const
 {
   // Do we even have an image?
   if (mImage.IsEmpty()) {
     return false;
   }
 
   return mPosition.DependsOnPositioningAreaSize() ||
-      mSize.DependsOnPositioningAreaSize(mImage) ||
+      mSize.DependsOnPositioningAreaSize(aFrame, mImage) ||
       mRepeat.DependsOnPositioningAreaSize();
 }
 
 bool
 nsStyleImageLayers::Layer::operator==(const Layer& aOther) const
 {
   return mAttachment == aOther.mAttachment &&
          mClip == aOther.mClip &&
@@ -3685,35 +3679,34 @@ nsStyleDisplay::~nsStyleDisplay()
 }
 
 void
 nsStyleDisplay::FinishStyle(
     nsPresContext* aPresContext, const nsStyleDisplay* aOldStyle)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (mShapeOutside.GetType() == StyleShapeSourceType::Image) {
-    const UniquePtr<nsStyleImage>& shapeImage = mShapeOutside.GetShapeImage();
-    if (shapeImage) {
-      // Bug 1434963: The CORS mode should instead be set when the
-      // ImageValue is created, in both Gecko and Stylo. That will
-      // avoid doing a mutation here.
-      if (shapeImage->GetType() == eStyleImageType_Image) {
-        shapeImage->ImageRequest()->GetImageValue()->SetCORSMode(
-          CORSMode::CORS_ANONYMOUS);
-      }
-      const nsStyleImage* oldShapeImage =
-        (aOldStyle &&
-         aOldStyle->mShapeOutside.GetType() == StyleShapeSourceType::Image)
-          ?  &*aOldStyle->mShapeOutside.GetShapeImage() : nullptr;
-      shapeImage->ResolveImage(aPresContext, oldShapeImage);
-    }
-  }
-
   GenerateCombinedTransform();
+
+  nsStyleImage* shapeImage = mShapeOutside.GetShapeImage();
+  if (!shapeImage) {
+    return;
+  }
+
+  // Bug 1434963: The CORS mode should instead be set when the
+  // ImageValue is created, in both Gecko and Stylo. That will
+  // avoid doing a mutation here.
+  if (shapeImage->GetType() == eStyleImageType_Image) {
+    shapeImage->ImageRequest()->GetImageValue()->SetCORSMode(
+      CORSMode::CORS_ANONYMOUS);
+  }
+
+  const nsStyleImage* oldShapeImage =
+    aOldStyle ? aOldStyle->mShapeOutside.GetShapeImage() : nullptr;
+  shapeImage->ResolveImage(aPresContext, oldShapeImage);
 }
 
 static inline nsChangeHint
 CompareTransformValues(const RefPtr<nsCSSValueSharedList>& aList,
                        const RefPtr<nsCSSValueSharedList>& aNewList)
 {
   nsChangeHint result = nsChangeHint(0);
   if (!aList != !aNewList || (aList && *aList != *aNewList)) {
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -260,21 +260,24 @@ public:
 
   // Can be called from any thread, but Resolve() must be called later
   // on the main thread before get() can be used.
   nsStyleImageRequest(Mode aModeFlags, mozilla::css::ImageValue* aImageValue);
 
   bool Resolve(nsPresContext*, const nsStyleImageRequest* aOldImageRequest);
   bool IsResolved() const { return mResolved; }
 
+  // NOTE(emilio): Please don't add callers of this outside of nsIFrame, this is
+  // going away soon.
   imgRequestProxy* get() {
     MOZ_ASSERT(IsResolved(), "Resolve() must be called first");
     MOZ_ASSERT(NS_IsMainThread());
     return mRequestProxy.get();
   }
+
   const imgRequestProxy* get() const {
     return const_cast<nsStyleImageRequest*>(this)->get();
   }
 
   // Returns whether the ImageValue objects in the two nsStyleImageRequests
   // return true from URLValueData::DefinitelyEqualURIs.
   bool DefinitelyEquals(const nsStyleImageRequest& aOther) const;
 
@@ -372,19 +375,16 @@ struct nsStyleImage
   nsStyleImageType GetType() const {
     return mType;
   }
   nsStyleImageRequest* ImageRequest() const {
     MOZ_ASSERT(mType == eStyleImageType_Image, "Data is not an image!");
     MOZ_ASSERT(mImage);
     return mImage;
   }
-  imgRequestProxy* GetImageData() const {
-    return ImageRequest()->get();
-  }
   nsStyleGradient* GetGradientData() const {
     NS_ASSERTION(mType == eStyleImageType_Gradient, "Data is not a gradient!");
     return mGradient;
   }
   bool IsResolved() const {
     return mType != eStyleImageType_Image || ImageRequest()->IsResolved();
   }
   const nsAtom* GetElementId() const {
@@ -405,43 +405,44 @@ struct nsStyleImage
    * Compute the actual crop rect in pixels, using the source image bounds.
    * The computation involves converting percentage unit to pixel unit and
    * clamping each side value to fit in the source image bounds.
    * @param aActualCropRect the computed actual crop rect.
    * @param aIsEntireImage true iff |aActualCropRect| is identical to the
    * source image bounds.
    * @return true iff |aActualCropRect| holds a meaningful value.
    */
-  bool ComputeActualCropRect(nsIntRect& aActualCropRect,
-                               bool* aIsEntireImage = nullptr) const;
+  bool ComputeActualCropRect(nsIFrame* aForFrame,
+                             nsIntRect& aActualCropRect,
+                             bool* aIsEntireImage = nullptr) const;
 
   /**
    * Starts the decoding of a image. Returns true if the current frame of the
    * image is complete. The return value is intended to be used instead of
    * IsComplete because IsComplete may not be up to date if notifications
    * from decoding are pending because they are being sent async.
    */
-  bool StartDecoding() const;
+  bool StartDecoding(nsIFrame* aForFrame) const;
   /**
    * @return true if the item is definitely opaque --- i.e., paints every
    * pixel within its bounds opaquely, and the bounds contains at least a pixel.
    */
-  bool IsOpaque() const;
+  bool IsOpaque(nsIFrame* aForFrame) const;
   /**
    * @return true if this image is fully loaded, and its size is calculated;
    * always returns true if |mType| is |eStyleImageType_Gradient| or
    * |eStyleImageType_Element|.
    */
-  bool IsComplete() const;
+  bool IsComplete(nsIFrame* aForFrame) const;
   /**
    * @return true if this image is loaded without error;
    * always returns true if |mType| is |eStyleImageType_Gradient| or
    * |eStyleImageType_Element|.
    */
-  bool IsLoaded() const;
+  bool IsLoaded(nsIFrame* aForFrame) const;
   /**
    * @return true if it is 100% confident that this image contains no pixel
    * to draw.
    */
   bool IsEmpty() const {
     // There are some other cases when the image will be empty, for example
     // when the crop rect is empty. However, checking the emptiness of crop
     // rect is non-trivial since each side value can be specified with
@@ -452,23 +453,16 @@ struct nsStyleImage
     return mType == eStyleImageType_Null;
   }
 
   bool operator==(const nsStyleImage& aOther) const;
   bool operator!=(const nsStyleImage& aOther) const {
     return !(*this == aOther);
   }
 
-  bool ImageDataEquals(const nsStyleImage& aOther) const
-  {
-    return GetType() == eStyleImageType_Image &&
-           aOther.GetType() == eStyleImageType_Image &&
-           GetImageData() == aOther.GetImageData();
-  }
-
   // These methods are used for the caller to caches the sub images created
   // during a border-image paint operation
   inline void SetSubImage(uint8_t aIndex, imgIContainer* aSubImage) const;
   inline imgIContainer* GetSubImage(uint8_t aIndex) const;
   void PurgeCacheForViewportChange(
     const mozilla::Maybe<nsSize>& aSVGViewportSize,
     const bool aHasIntrinsicRatio) const;
 
@@ -583,17 +577,18 @@ struct nsStyleImageLayers {
       eLengthPercentage,
       eDimensionType_COUNT
     };
     uint8_t mWidthType, mHeightType;
 
     // True if the effective image size described by this depends on the size of
     // the corresponding frame, when aImage (which must not have null type) is
     // the background image.
-    bool DependsOnPositioningAreaSize(const nsStyleImage& aImage) const;
+    bool DependsOnPositioningAreaSize(nsIFrame* aFrame,
+                                      const nsStyleImage& aImage) const;
 
     // Initialize nothing
     Size() {}
 
     // Initialize to initial values
     void SetInitialValues();
 
     bool operator==(const Size& aOther) const;
@@ -682,17 +677,17 @@ struct nsStyleImageLayers {
       mImage.ResolveImage(aContext, aOldLayer ? &aOldLayer->mImage : nullptr);
     }
 
     // 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.
-    bool RenderingMightDependOnPositioningAreaSizeChange() const;
+    bool RenderingMightDependOnPositioningAreaSizeChange(nsIFrame*) const;
 
     // Compute the change hint required by changes in just this layer.
     nsChangeHint CalcDifference(const Layer& aNewLayer) const;
 
     // An equality operator that compares the images using URL-equality
     // rather than pointer-equality.
     bool operator==(const Layer& aOther) const;
     bool operator!=(const Layer& aOther) const {
@@ -988,16 +983,21 @@ static bool IsVisibleBorderStyle(uint8_t
 }
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleBorder
 {
   explicit nsStyleBorder(const nsPresContext* aContext);
   nsStyleBorder(const nsStyleBorder& aBorder);
   ~nsStyleBorder();
 
+  bool IsBorderImageLoaded(nsIFrame* aForFrame) const
+  {
+    return mBorderImageSource.IsLoaded(aForFrame);
+  }
+
   // 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*, const nsStyleBorder*);
   const static bool kHasFinishStyle = true;
 
   nsChangeHint CalcDifference(const nsStyleBorder& aNewData) const;
 
@@ -1053,31 +1053,18 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   void SetBorderStyle(mozilla::Side aSide, uint8_t aStyle)
   {
     NS_ASSERTION(aSide <= mozilla::eSideLeft, "bad side");
     mBorderStyle[aSide] = aStyle;
     mComputedBorder.Side(aSide) =
       (HasVisibleStyle(aSide) ? mBorder.Side(aSide) : 0);
   }
 
-  inline bool IsBorderImageLoaded() const
-  {
-    return mBorderImageSource.IsLoaded();
-  }
-
   nsMargin GetImageOutset() const;
 
-  imgIRequest* GetBorderImageRequest() const
-  {
-    if (mBorderImageSource.GetType() == eStyleImageType_Image) {
-      return mBorderImageSource.GetImageData();
-    }
-    return nullptr;
-  }
-
 public:
   nsStyleCorners mBorderRadius;       // [reset] coord, percent
   nsStyleImage   mBorderImageSource;  // [reset]
   nsStyleSides   mBorderImageSlice;   // [reset] factor, percent
   nsStyleSides   mBorderImageWidth;   // [reset] length, factor, percent, auto
   nsStyleSides   mBorderImageOutset;  // [reset] length, factor
 
   uint8_t        mBorderImageFill;    // [reset]
@@ -1234,21 +1221,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsChangeHint CalcDifference(const nsStyleList& aNewData,
                               const nsStyleDisplay* aOldDisplay) const;
 
   static void Shutdown() {
     sInitialQuotes = nullptr;
     sNoneQuotes = nullptr;
   }
 
-  imgRequestProxy* GetListStyleImage() const
-  {
-    return mListStyleImage ? mListStyleImage->get() : nullptr;
-  }
-
   already_AddRefed<nsIURI> GetListStyleImageURI() const;
 
   const nsStyleQuoteValues::QuotePairArray& GetQuotePairs() const;
 
   void SetQuotesInherit(const nsStyleList* aOther);
   void SetQuotesInitial();
   void SetQuotesNone();
   void SetQuotes(nsStyleQuoteValues::QuotePairArray&& aValues);
@@ -2113,26 +2095,27 @@ struct StyleShapeSource final
     MOZ_ASSERT(mType == StyleShapeSourceType::URL, "Wrong shape source type!");
     return mShapeImage
       ? static_cast<css::URLValue*>(mShapeImage->GetURLValue())
       : nullptr;
   }
 
   void SetURL(css::URLValue* aValue);
 
-  const UniquePtr<nsStyleImage>& GetShapeImage() const
+  nsStyleImage& ShapeImage() const
   {
-    MOZ_ASSERT(mType == StyleShapeSourceType::Image, "Wrong shape source type!");
-    return mShapeImage;
+    MOZ_ASSERT(mType == StyleShapeSourceType::Image);
+    MOZ_ASSERT(mShapeImage);
+    return *mShapeImage;
   }
 
-  // Iff we have "shape-outside:<image>" with an image URI (not a gradient),
-  // this method returns the corresponding imgIRequest*. Else, returns
-  // null.
-  imgIRequest* GetShapeImageData() const;
+  nsStyleImage* GetShapeImage() const
+  {
+    return mType == StyleShapeSourceType::Image ? &ShapeImage() : nullptr;
+  }
 
   void SetShapeImage(UniquePtr<nsStyleImage> aShapeImage);
 
   const UniquePtr<StyleBasicShape>& GetBasicShape() const
   {
     MOZ_ASSERT(mType == StyleShapeSourceType::Shape, "Wrong shape source type!");
     return mBasicShape;
   }
@@ -2669,21 +2652,16 @@ public:
 
   nsStyleImageRequest* ImageRequest() const
   {
     MOZ_ASSERT(mType == eStyleContentType_Image);
     MOZ_ASSERT(mContent.mImage);
     return mContent.mImage;
   }
 
-  imgRequestProxy* GetImage() const
-  {
-    return ImageRequest()->get();
-  }
-
   void SetKeyword(nsStyleContentType aType)
   {
     MOZ_ASSERT(aType == eStyleContentType_OpenQuote ||
                aType == eStyleContentType_CloseQuote ||
                aType == eStyleContentType_NoOpenQuote ||
                aType == eStyleContentType_NoCloseQuote ||
                aType == eStyleContentType_AltContent);
     MOZ_ASSERT(mType == eStyleContentType_Uninitialized,
@@ -2843,20 +2821,16 @@ struct nsCursorImage
 
   nsCursorImage& operator=(const nsCursorImage& aOther);
 
   bool operator==(const nsCursorImage& aOther) const;
   bool operator!=(const nsCursorImage& aOther) const
   {
     return !(*this == aOther);
   }
-
-  imgRequestProxy* GetImage() const {
-    return mImage->get();
-  }
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUserInterface
 {
   explicit nsStyleUserInterface(const nsPresContext* aContext);
   nsStyleUserInterface(const nsStyleUserInterface& aOther);
   ~nsStyleUserInterface();
 
--- a/layout/svg/SVGObserverUtils.cpp
+++ b/layout/svg/SVGObserverUtils.cpp
@@ -407,26 +407,28 @@ nsSVGMaskProperty::nsSVGMaskProperty(nsI
 }
 
 void
 nsSVGMaskProperty::ResolveImage(uint32_t aIndex)
 {
   const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset();
   MOZ_ASSERT(aIndex < svgReset->mMask.mImageCount);
 
+  // FIXME(emilio): const_cast is evil. Also, stop calling ResolveImage here
+  // manually.
   nsStyleImage& image =
     const_cast<nsStyleImage&>(svgReset->mMask.mLayers[aIndex].mImage);
 
   if (!image.IsResolved()) {
     MOZ_ASSERT(image.GetType() == nsStyleImageType::eStyleImageType_Image);
     image.ResolveImage(mFrame->PresContext(), nullptr);
 
     mozilla::css::ImageLoader* imageLoader =
       mFrame->PresContext()->Document()->StyleImageLoader();
-    if (imgRequestProxy* req = image.GetImageData()) {
+    if (imgRequestProxy* req = mFrame->GetImageRequest(image)) {
       imageLoader->AssociateRequestToFrame(req, mFrame, 0);
     }
   }
 }
 
 bool
 nsSVGTextPathProperty::TargetIsValid()
 {
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -727,17 +727,17 @@ nsSVGIntegrationUtils::IsMaskResourceRea
 
   for (uint32_t i = 0; i < maskFrames.Length(); i++) {
     // Refers to a valid SVG mask.
     if (maskFrames[i]) {
       continue;
     }
 
     // Refers to an external resource, which is not ready yet.
-    if (!svgReset->mMask.mLayers[i].mImage.IsComplete()) {
+    if (!svgReset->mMask.mLayers[i].mImage.IsComplete(firstFrame)) {
       return false;
     }
   }
 
   // Either all mask resources are ready, or no mask resource is needed.
   return true;
 }
 
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -267,17 +267,18 @@ nsImageBoxFrame::UpdateImage()
     }
   } else {
     // Only get the list-style-image if we aren't being drawn
     // by a native theme.
     uint8_t appearance = StyleDisplay()->mAppearance;
     if (!(appearance && nsBox::gTheme &&
           nsBox::gTheme->ThemeSupportsWidget(nullptr, this, appearance))) {
       // get the list-style-image
-      imgRequestProxy *styleRequest = StyleList()->GetListStyleImage();
+      imgRequestProxy* styleRequest =
+        GetImageRequest(StyleList()->mListStyleImage);
       if (styleRequest) {
         styleRequest->SyncClone(mListener,
                                 mContent->GetComposedDoc(),
                                 getter_AddRefs(mImageRequest));
       }
     }
   }
 
@@ -662,18 +663,19 @@ nsImageBoxFrame::DidSetComputedStyle(Com
   if (disp->mAppearance && nsBox::gTheme &&
       nsBox::gTheme->ThemeSupportsWidget(nullptr, this, disp->mAppearance))
     return;
 
   // If list-style-image changes, we have a new image.
   nsCOMPtr<nsIURI> oldURI, newURI;
   if (mImageRequest)
     mImageRequest->GetURI(getter_AddRefs(oldURI));
-  if (myList->GetListStyleImage())
-    myList->GetListStyleImage()->GetURI(getter_AddRefs(newURI));
+  if (auto* request = GetImageRequest(myList->mListStyleImage)) {
+    request->GetURI(getter_AddRefs(newURI));
+  }
   bool equal;
   if (newURI == oldURI ||   // handles null==null
       (newURI && oldURI &&
        NS_SUCCEEDED(newURI->Equals(oldURI, &equal)) && equal))
     return;
 
   UpdateImage();
 } // DidSetComputedStyle
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -2104,17 +2104,17 @@ nsTreeBodyFrame::GetImage(int32_t aRowIn
   mView->GetImageSrc(aRowIndex, aCol, imageSrc);
   RefPtr<imgRequestProxy> styleRequest;
   if (!aUseContext && !imageSrc.IsEmpty()) {
     aAllowImageRegions = false;
   }
   else {
     // Obtain the URL from the ComputedStyle.
     aAllowImageRegions = true;
-    styleRequest = aComputedStyle->StyleList()->GetListStyleImage();
+    styleRequest = GetImageRequest(aComputedStyle->StyleList()->mListStyleImage);
     if (!styleRequest)
       return NS_OK;
     nsCOMPtr<nsIURI> uri;
     styleRequest->GetURI(getter_AddRefs(uri));
     nsAutoCString spec;
     nsresult rv = uri->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
     CopyUTF8toUTF16(spec, imageSrc);