Back out Bug 679230 due to Android reftest failures.
authorKyle Huey <khuey@kylehuey.com>
Sat, 07 Apr 2012 10:36:49 -0700
changeset 94490 babbc38b7f521849e2ddb7859483f59cdd398e71
parent 94489 dd39182f70cbde594f6caf5ed6ce3e0b02a25665
child 94491 d25c5f07be7dcac0601e79e1f557aca9882244ee
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs679230
milestone14.0a1
first release with
nightly linux32
babbc38b7f52 / 14.0a1 / 20120408031146 / files
nightly linux64
babbc38b7f52 / 14.0a1 / 20120408031146 / files
nightly mac
babbc38b7f52 / 14.0a1 / 20120408031146 / files
nightly win32
babbc38b7f52 / 14.0a1 / 20120408031146 / files
nightly win64
babbc38b7f52 / 14.0a1 / 20120408031146 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Back out Bug 679230 due to Android reftest failures.
content/base/public/nsIDocument.h
content/base/public/nsINode.h
content/base/src/nsDocument.cpp
content/base/src/nsGenConImageContent.cpp
content/base/src/nsImageLoadingContent.cpp
content/base/src/nsImageLoadingContent.h
content/html/content/src/nsHTMLImageElement.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLObjectElement.cpp
content/html/content/src/nsHTMLSharedObjectElement.cpp
content/svg/content/src/nsSVGFilters.cpp
content/svg/content/src/nsSVGImageElement.cpp
image/public/Makefile.in
image/public/imgIOnloadBlocker.idl
image/src/imgRequest.cpp
image/src/imgRequest.h
image/src/imgRequestProxy.cpp
image/src/imgRequestProxy.h
image/src/imgStatusTracker.cpp
image/src/imgStatusTracker.h
image/test/unit/image_load_helpers.js
layout/base/Makefile.in
layout/base/nsCSSRendering.cpp
layout/base/nsImageLoader.cpp
layout/base/nsImageLoader.h
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.cpp
layout/generic/nsFrame.cpp
layout/reftests/backgrounds/really-big-background-ref.html
layout/reftests/backgrounds/really-big-background.html
layout/reftests/backgrounds/really-big-background.png
layout/reftests/backgrounds/reftest.list
layout/style/ImageLoader.cpp
layout/style/ImageLoader.h
layout/style/Makefile.in
layout/style/nsCSSDataBlock.cpp
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsRuleNode.cpp
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -111,28 +111,27 @@ class imgIRequest;
 class nsISHEntry;
 class nsDOMNavigationTiming;
 class nsWindowSizes;
 class nsIObjectLoadingContent;
 
 namespace mozilla {
 namespace css {
 class Loader;
-class ImageLoader;
 } // namespace css
 
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0xdb888523, 0x541f, 0x49e3, \
-  { 0xa9, 0x71, 0xb5, 0xea, 0xd1, 0xf0, 0xc3, 0xcf } }
+{ 0x8e51e6d9, 0x914d, 0x46ba, \
+  { 0xb3, 0x11, 0x2f, 0x27, 0x3d, 0xe6, 0x0d, 0x19 } }
 
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
@@ -640,23 +639,16 @@ public:
   /**
    * Get this document's CSSLoader.  This is guaranteed to not return null.
    */
   mozilla::css::Loader* CSSLoader() const {
     return mCSSLoader;
   }
 
   /**
-   * Get this document's StyleImageLoader.  This is guaranteed to not return null.
-   */
-  mozilla::css::ImageLoader* StyleImageLoader() const {
-    return mStyleImageLoader;
-  }
-
-  /**
    * Get the channel that was passed to StartDocumentLoad or Reset for this
    * document.  Note that this may be null in some cases (eg if
    * StartDocumentLoad or Reset were never called)
    */
   virtual nsIChannel* GetChannel() const = 0;
 
   /**
    * Get this document's attribute stylesheet.  May return null if
@@ -1742,17 +1734,16 @@ protected:
   // A reference to the element last returned from GetRootElement().
   mozilla::dom::Element* mCachedRootElement;
 
   // We'd like these to be nsRefPtrs, but that'd require us to include
   // additional headers that we don't want to expose.
   // The cleanup is handled by the nsDocument destructor.
   nsNodeInfoManager* mNodeInfoManager; // [STRONG]
   mozilla::css::Loader* mCSSLoader; // [STRONG]
-  mozilla::css::ImageLoader* mStyleImageLoader; // [STRONG]
   nsHTMLStyleSheet* mAttrStyleSheet;
 
   // The set of all object, embed, applet, video and audio elements for
   // which this is the owner document. (They might not be in the document.)
   // These are non-owning pointers, the elements are responsible for removing
   // themselves when they go away.
   nsAutoPtr<nsTHashtable<nsPtrHashKey<nsIContent> > > mFreezableElements;
   
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -1653,31 +1653,16 @@ extern const nsIID kThisPtrOffsetsSID;
     NS_INTERFACE_TABLE_ENTRY(_class, _i4)                                     \
     NS_INTERFACE_TABLE_ENTRY(_class, _i5)                                     \
     NS_INTERFACE_TABLE_ENTRY(_class, _i6)                                     \
     NS_INTERFACE_TABLE_ENTRY(_class, _i7)                                     \
     NS_INTERFACE_TABLE_ENTRY(_class, _i8)                                     \
   NS_OFFSET_AND_INTERFACE_TABLE_END                                           \
   NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
 
-#define NS_NODE_INTERFACE_TABLE9(_class, _i1, _i2, _i3, _i4, _i5, _i6, _i7,   \
-                                 _i8, _i9)                                    \
-  NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(_class)                            \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i1)                                     \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i2)                                     \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i3)                                     \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i4)                                     \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i5)                                     \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i6)                                     \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i7)                                     \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i8)                                     \
-    NS_INTERFACE_TABLE_ENTRY(_class, _i9)                                     \
-  NS_OFFSET_AND_INTERFACE_TABLE_END                                           \
-  NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
-
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsINode, NS_INODE_IID)
 
 
 #define NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER \
   nsContentUtils::TraceWrapper(tmp, aCallback, aClosure);
 
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -61,17 +61,16 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsDocument.h"
 #include "nsUnicharUtils.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsContentList.h"
 #include "nsIObserver.h"
 #include "nsIBaseWindow.h"
 #include "mozilla/css/Loader.h"
-#include "mozilla/css/ImageLoader.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIScriptRuntime.h"
 #include "nsCOMArray.h"
 
 #include "nsGUIEvent.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsIDOMNodeFilter.h"
@@ -1645,21 +1644,16 @@ nsDocument::~nsDocument()
   }
 
   if (mCSSLoader) {
     // Could be null here if Init() failed
     mCSSLoader->DropDocumentReference();
     NS_RELEASE(mCSSLoader);
   }
 
-  if (mStyleImageLoader) {
-    mStyleImageLoader->DropDocumentReference();
-    NS_RELEASE(mStyleImageLoader);
-  }
-
   // XXX Ideally we'd do this cleanup in the nsIDocument destructor.
   if (mNodeInfoManager) {
     mNodeInfoManager->DropDocumentReference();
   }
 
   delete mHeaderData;
 
   if (mBoxObjectTable) {
@@ -1984,17 +1978,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   
   tmp->mInUnlinkOrDeletion = false;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 nsresult
 nsDocument::Init()
 {
-  if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
+  if (mCSSLoader || mNodeInfoManager || mScriptLoader) {
     return NS_ERROR_ALREADY_INITIALIZED;
   }
 
   mIdentifierMap.Init();
   (void)mStyledLinks.Init();
   mRadioGroups.Init();
 
   // Force initialization.
@@ -2013,26 +2007,20 @@ nsDocument::Init()
   NS_ENSURE_TRUE(mOnloadBlocker, NS_ERROR_OUT_OF_MEMORY);
 
   mCSSLoader = new mozilla::css::Loader(this);
   NS_ENSURE_TRUE(mCSSLoader, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(mCSSLoader);
   // Assume we're not quirky, until we know otherwise
   mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
 
-  mStyleImageLoader = new mozilla::css::ImageLoader(this);
-  NS_ADDREF(mStyleImageLoader);
-
-  nsresult rv = mStyleImageLoader->Init();
-  NS_ENSURE_SUCCESS(rv, rv);
-
   mNodeInfoManager = new nsNodeInfoManager();
   NS_ENSURE_TRUE(mNodeInfoManager, NS_ERROR_OUT_OF_MEMORY);
 
-  rv = mNodeInfoManager->Init(this);
+  nsresult  rv = mNodeInfoManager->Init(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // mNodeInfo keeps NodeInfoManager alive!
   mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
   NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
   NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE,
                     "Bad NodeType in aNodeInfo");
 
--- a/content/base/src/nsGenConImageContent.cpp
+++ b/content/base/src/nsGenConImageContent.cpp
@@ -71,22 +71,18 @@ public:
   
 private:
   virtual ~nsGenConImageContent();
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 };
 
-NS_IMPL_ISUPPORTS_INHERITED4(nsGenConImageContent,
-                             nsXMLElement,
-                             nsIImageLoadingContent,
-                             imgIContainerObserver,
-                             imgIDecoderObserver,
-                             imgIOnloadBlocker)
+NS_IMPL_ISUPPORTS_INHERITED3(nsGenConImageContent, nsXMLElement,
+                             nsIImageLoadingContent, imgIContainerObserver, imgIDecoderObserver)
 
 nsresult
 NS_NewGenConImageContent(nsIContent** aResult, already_AddRefed<nsINodeInfo> aNodeInfo,
                          imgIRequest* aImageRequest)
 {
   NS_PRECONDITION(aImageRequest, "Must have request!");
   nsGenConImageContent *it = new nsGenConImageContent(aNodeInfo);
   if (!it)
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -110,16 +110,17 @@ nsImageLoadingContent::nsImageLoadingCon
     mImageBlockingStatus(nsIContentPolicy::ACCEPT),
     mLoadingEnabled(true),
     mIsImageStateForced(false),
     mLoading(false),
     // mBroken starts out true, since an image without a URI is broken....
     mBroken(true),
     mUserDisabled(false),
     mSuppressed(false),
+    mBlockingOnload(false),
     mNewRequestsWillNeedAnimationReset(false),
     mPendingRequestNeedsResetAnimation(false),
     mCurrentRequestNeedsResetAnimation(false),
     mStateChangerDepth(0),
     mCurrentRequestRegistered(false),
     mPendingRequestRegistered(false)
 {
   if (!nsContentUtils::GetImgLoader()) {
@@ -181,16 +182,33 @@ nsImageLoadingContent::OnStartRequest(im
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::OnStartDecode(imgIRequest* aRequest)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
+  // Onload blocking. This only applies for the current request.
+  if (aRequest == mCurrentRequest) {
+
+    // Determine whether this is a background request (this can be the case
+    // with multipart/x-mixed-replace images, for example).
+    PRUint32 loadFlags;
+    nsresult rv = aRequest->GetLoadFlags(&loadFlags);
+    bool background =
+      (NS_SUCCEEDED(rv) && (loadFlags & nsIRequest::LOAD_BACKGROUND));
+
+    // Block onload for non-background requests
+    if (!background) {
+      NS_ABORT_IF_FALSE(!mBlockingOnload, "Shouldn't already be blocking");
+      SetBlockingOnload(true);
+    }
+  }
+
   LOOP_OVER_OBSERVERS(OnStartDecode(aRequest));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::OnStartContainer(imgIRequest* aRequest,
                                         imgIContainer* aContainer)
 {
@@ -226,26 +244,39 @@ nsImageLoadingContent::OnDataAvailable(i
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::OnStopFrame(imgIRequest* aRequest,
                                    PRUint32 aFrame)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
+  // If we're blocking a load, one frame is enough
+  if (aRequest == mCurrentRequest)
+    SetBlockingOnload(false);
+
   LOOP_OVER_OBSERVERS(OnStopFrame(aRequest, aFrame));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::OnStopContainer(imgIRequest* aRequest,
                                        imgIContainer* aContainer)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
+  // This is really hacky. We need to handle the case where we start decoding,
+  // block onload, but then hit an error before we get to our first frame. In
+  // theory we would just hook in at OnStopDecode, but OnStopDecode is broken
+  // until we fix bug 505385. OnStopContainer is actually going away at that
+  // point. So for now we take advantage of the fact that OnStopContainer is
+  // always fired in the decoders at the same time as OnStopDecode.
+  if (aRequest == mCurrentRequest)
+    SetBlockingOnload(false);
+
   LOOP_OVER_OBSERVERS(OnStopContainer(aRequest, aContainer));
   return NS_OK;
 }
 
 // Warning - This isn't actually fired when decode is complete. Rather, it's
 // fired when load is complete. See bug 505385, and in the mean time use
 // OnStopContainer.
 NS_IMETHODIMP
@@ -313,17 +344,17 @@ nsImageLoadingContent::OnStopDecode(imgI
 
   // Fire the appropriate DOM event.
   if (NS_SUCCEEDED(aStatus)) {
     FireEvent(NS_LITERAL_STRING("load"));
   } else {
     FireEvent(NS_LITERAL_STRING("error"));
   }
 
-  nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsINode> thisNode = do_QueryInterface(this);
   nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, bool aLastPart)
 {
@@ -601,46 +632,16 @@ NS_IMETHODIMP nsImageLoadingContent::For
   GetCurrentURI(getter_AddRefs(currentURI));
   if (!currentURI) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   return LoadImage(currentURI, true, true, nsnull, nsIRequest::VALIDATE_ALWAYS);
 }
 
-NS_IMETHODIMP
-nsImageLoadingContent::BlockOnload(imgIRequest* aRequest)
-{
-  if (aRequest != mCurrentRequest) {
-    return NS_OK;
-  }
-
-  nsIDocument* doc = GetOurDocument();
-  if (doc) {
-    doc->BlockOnload();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsImageLoadingContent::UnblockOnload(imgIRequest* aRequest)
-{
-  if (aRequest != mCurrentRequest) {
-    return NS_OK;
-  }
-
-  nsIDocument* doc = GetOurDocument();
-  if (doc) {
-    doc->UnblockOnload(false);
-  }
-
-  return NS_OK;
-}
-
 /*
  * Non-interface methods
  */
 
 void
 nsImageLoadingContent::NotifyOwnerDocumentChanged(nsIDocument *aOldDoc)
 {
   // If we had a document before, unregister ourselves with it.
@@ -740,29 +741,26 @@ nsImageLoadingContent::LoadImage(nsIURI*
   // From this point on, our image state could change. Watch it.
   AutoStateChanger changer(this, aNotify);
 
   // Sanity check.
   //
   // We use the principal of aDocument to avoid having to QI |this| an extra
   // time. It should always be the same as the principal of this node.
 #ifdef DEBUG
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
   NS_ABORT_IF_FALSE(thisContent &&
                     thisContent->NodePrincipal() == aDocument->NodePrincipal(),
                     "Principal mismatch?");
 #endif
 
   // Are we blocked?
   PRInt16 cpDecision = nsIContentPolicy::REJECT_REQUEST;
-  nsContentUtils::CanLoadImage(aNewURI,
-                               static_cast<nsIImageLoadingContent*>(this),
-                               aDocument,
-                               aDocument->NodePrincipal(),
-                               &cpDecision);
+  nsContentUtils::CanLoadImage(aNewURI, this, aDocument,
+                               aDocument->NodePrincipal(), &cpDecision);
   if (!NS_CP_ACCEPTED(cpDecision)) {
     FireEvent(NS_LITERAL_STRING("error"));
     SetBlockedRequest(aNewURI, cpDecision);
     return NS_OK;
   }
 
   nsLoadFlags loadFlags = aLoadFlags;
   PRInt32 corsmode = GetCORSMode();
@@ -857,17 +855,17 @@ nsImageLoadingContent::UpdateImageState(
     // changer is destroyed. Need this to work around the fact that some libpr0n
     // stuff is actually sync and hence we can get OnStopDecode called while
     // we're still under LoadImage, and OnStopDecode doesn't know anything about
     // aNotify.
     // XXX - This machinery should be removed after bug 521604.
     return;
   }
   
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
   if (!thisContent) {
     return;
   }
 
   mLoading = mBroken = mUserDisabled = mSuppressed = false;
   
   // If we were blocked by server-based content policy, we claim to be
   // suppressed.  If we were blocked by type-based content policy, we claim to
@@ -921,26 +919,26 @@ nsImageLoadingContent::UseAsPrimaryReque
     return rv;
 
   return NS_OK;
 }
 
 nsIDocument*
 nsImageLoadingContent::GetOurDocument()
 {
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
   NS_ENSURE_TRUE(thisContent, nsnull);
 
   return thisContent->OwnerDoc();
 }
 
 nsIFrame*
 nsImageLoadingContent::GetOurPrimaryFrame()
 {
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
   return thisContent->GetPrimaryFrame();
 }
 
 nsPresContext* nsImageLoadingContent::GetFramePresContext()
 {
   nsIFrame* frame = GetOurPrimaryFrame();
   if (!frame) {
     return nsnull;
@@ -953,17 +951,17 @@ nsresult
 nsImageLoadingContent::StringToURI(const nsAString& aSpec,
                                    nsIDocument* aDocument,
                                    nsIURI** aURI)
 {
   NS_PRECONDITION(aDocument, "Must have a document");
   NS_PRECONDITION(aURI, "Null out param");
 
   // (1) Get the base URI
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
   NS_ASSERTION(thisContent, "An image loading content must be an nsIContent");
   nsCOMPtr<nsIURI> baseURL = thisContent->GetBaseURI();
 
   // (2) Get the charset
   const nsAFlatCString &charset = aDocument->GetDocumentCharacterSet();
 
   // (3) Construct the silly thing
   return NS_NewURI(aURI,
@@ -975,17 +973,17 @@ nsImageLoadingContent::StringToURI(const
 
 nsresult
 nsImageLoadingContent::FireEvent(const nsAString& aEventType)
 {
   // We have to fire the event asynchronously so that we won't go into infinite
   // loops in cases when onLoad handlers reset the src and the new src is in
   // cache.
 
-  nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsINode> thisNode = do_QueryInterface(this);
 
   nsRefPtr<nsAsyncDOMEvent> event =
     new nsLoadBlockingAsyncDOMEvent(thisNode, aEventType, false, false);
   event->PostDOMEvent();
   
   return NS_OK;
 }
 
@@ -1083,16 +1081,20 @@ nsImageLoadingContent::ClearCurrentReque
   nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
                                         &mCurrentRequestRegistered);
 
   // Clean up the request.
   UntrackImage(mCurrentRequest);
   mCurrentRequest->CancelAndForgetObserver(aReason);
   mCurrentRequest = nsnull;
   mCurrentRequestNeedsResetAnimation = false;
+
+  // We only block onload during the decoding of "current" images. This one is
+  // going away, so we should unblock unconditionally here.
+  SetBlockingOnload(false);
 }
 
 void
 nsImageLoadingContent::ClearPendingRequest(nsresult aReason)
 {
   if (!mPendingRequest)
     return;
 
@@ -1133,16 +1135,38 @@ nsImageLoadingContent::HaveSize(imgIRequ
     return false;
 
   // Query the image
   PRUint32 status;
   nsresult rv = aImage->GetImageStatus(&status);
   return (NS_SUCCEEDED(rv) && (status & imgIRequest::STATUS_SIZE_AVAILABLE));
 }
 
+void
+nsImageLoadingContent::SetBlockingOnload(bool aBlocking)
+{
+  // If we're already in the desired state, we have nothing to do
+  if (mBlockingOnload == aBlocking)
+    return;
+
+  // Get the document
+  nsIDocument* doc = GetOurDocument();
+
+  if (doc) {
+    // Take the appropriate action
+    if (aBlocking)
+      doc->BlockOnload();
+    else
+      doc->UnblockOnload(false);
+
+    // Update our state
+    mBlockingOnload = aBlocking;
+  }
+}
+
 nsresult
 nsImageLoadingContent::TrackImage(imgIRequest* aImage)
 {
   if (!aImage)
     return NS_OK;
 
   nsIDocument* doc = GetOurDocument();
   if (doc)
--- a/content/base/src/nsImageLoadingContent.h
+++ b/content/base/src/nsImageLoadingContent.h
@@ -42,41 +42,38 @@
  * loading functionality (eg <img>, <object>, etc).
  */
 
 #ifndef nsImageLoadingContent_h__
 #define nsImageLoadingContent_h__
 
 #include "imgIContainerObserver.h"
 #include "imgIDecoderObserver.h"
-#include "imgIOnloadBlocker.h"
 #include "mozilla/CORSMode.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h" // NS_CONTENT_DELETE_LIST_MEMBER
 #include "nsEventStates.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIRequest.h"
 
 class nsIURI;
 class nsIDocument;
 class imgILoader;
 class nsIIOService;
 
-class nsImageLoadingContent : public nsIImageLoadingContent,
-                              public imgIOnloadBlocker
+class nsImageLoadingContent : public nsIImageLoadingContent
 {
   /* METHODS */
 public:
   nsImageLoadingContent();
   virtual ~nsImageLoadingContent();
 
   NS_DECL_IMGICONTAINEROBSERVER
   NS_DECL_IMGIDECODEROBSERVER
   NS_DECL_NSIIMAGELOADINGCONTENT
-  NS_DECL_IMGIONLOADBLOCKER
 
 protected:
   /**
    * LoadImage is called by subclasses when the appropriate
    * attributes (eg 'src' for <img> tags) change.  The string passed
    * in is the new uri string; this consolidates the code for getting
    * the charset, constructing URI objects, and any other incidentals
    * into this superclass.   
@@ -373,16 +370,21 @@ private:
    * The state we had the last time we checked whether we needed to notify the
    * document of a state change.  These are maintained by UpdateImageState.
    */
   bool mLoading : 1;
   bool mBroken : 1;
   bool mUserDisabled : 1;
   bool mSuppressed : 1;
 
+  /**
+   * Whether we're currently blocking document load.
+   */
+  bool mBlockingOnload : 1;
+
 protected:
   /**
    * A hack to get animations to reset, see bug 594771. On requests
    * that originate from setting .src, we mark them for needing their animation
    * reset when they are ready. mNewRequestsWillNeedAnimationReset is set to
    * true while preparing such requests (as a hack around needing to change an
    * interface), and the other two booleans store which of the current
    * and pending requests are of the sort that need their animation restarted.
--- a/content/html/content/src/nsHTMLImageElement.cpp
+++ b/content/html/content/src/nsHTMLImageElement.cpp
@@ -214,23 +214,22 @@ nsHTMLImageElement::~nsHTMLImageElement(
 NS_IMPL_ADDREF_INHERITED(nsHTMLImageElement, nsGenericElement)
 NS_IMPL_RELEASE_INHERITED(nsHTMLImageElement, nsGenericElement)
 
 
 DOMCI_NODE_DATA(HTMLImageElement, nsHTMLImageElement)
 
 // QueryInterface implementation for nsHTMLImageElement
 NS_INTERFACE_TABLE_HEAD(nsHTMLImageElement)
-  NS_HTML_CONTENT_INTERFACE_TABLE6(nsHTMLImageElement,
+  NS_HTML_CONTENT_INTERFACE_TABLE5(nsHTMLImageElement,
                                    nsIDOMHTMLImageElement,
                                    nsIJSNativeInitializer,
                                    imgIDecoderObserver,
                                    nsIImageLoadingContent,
-                                   imgIContainerObserver,
-                                   imgIOnloadBlocker)
+                                   imgIContainerObserver)
   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLImageElement,
                                                nsGenericHTMLElement)
 NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLImageElement)
 
 
 NS_IMPL_ELEMENT_CLONE(nsHTMLImageElement)
 
 
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -650,24 +650,23 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_ADDREF_INHERITED(nsHTMLInputElement, nsGenericElement) 
 NS_IMPL_RELEASE_INHERITED(nsHTMLInputElement, nsGenericElement) 
 
 
 DOMCI_NODE_DATA(HTMLInputElement, nsHTMLInputElement)
 
 // QueryInterface implementation for nsHTMLInputElement
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLInputElement)
-  NS_HTML_CONTENT_INTERFACE_TABLE9(nsHTMLInputElement,
+  NS_HTML_CONTENT_INTERFACE_TABLE8(nsHTMLInputElement,
                                    nsIDOMHTMLInputElement,
                                    nsITextControlElement,
                                    nsIPhonetic,
                                    imgIDecoderObserver,
                                    nsIImageLoadingContent,
                                    imgIContainerObserver,
-                                   imgIOnloadBlocker,
                                    nsIDOMNSEditableElement,
                                    nsIConstraintValidation)
   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLInputElement,
                                                nsGenericHTMLFormElement)
 NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLInputElement)
 
 // nsIConstraintValidation
 NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(nsHTMLInputElement)
--- a/content/html/content/src/nsHTMLObjectElement.cpp
+++ b/content/html/content/src/nsHTMLObjectElement.cpp
@@ -228,17 +228,16 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION
   NS_HTML_CONTENT_INTERFACE_TABLE_BEGIN(nsHTMLObjectElement)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIDOMHTMLObjectElement)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, imgIDecoderObserver)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIRequestObserver)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIStreamListener)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIFrameLoaderOwner)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIObjectLoadingContent)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIImageLoadingContent)
-    NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, imgIOnloadBlocker)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, imgIContainerObserver)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIInterfaceRequestor)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIChannelEventSink)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIConstraintValidation)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLObjectElement, nsIDOMGetSVGDocument)
   NS_OFFSET_AND_INTERFACE_TABLE_END
   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLObjectElement,
                                                nsGenericHTMLFormElement)
--- a/content/html/content/src/nsHTMLSharedObjectElement.cpp
+++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp
@@ -253,17 +253,16 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION
                                                   nsIDOMHTMLAppletElement)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIRequestObserver)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIStreamListener)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIFrameLoaderOwner)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, imgIContainerObserver)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIObjectLoadingContent)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, imgIDecoderObserver)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIImageLoadingContent)
-    NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, imgIOnloadBlocker)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIInterfaceRequestor)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLSharedObjectElement, nsIChannelEventSink)
   NS_OFFSET_AND_INTERFACE_TABLE_END
   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE_AMBIGUOUS(nsHTMLSharedObjectElement,
                                                          nsGenericHTMLElement,
                                                          nsIDOMHTMLAppletElement)
   NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLAppletElement, applet)
   NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLEmbedElement, embed)
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -5458,22 +5458,21 @@ NS_IMPL_NS_NEW_SVG_ELEMENT(FEImage)
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGFEImageElement,nsSVGFEImageElementBase)
 NS_IMPL_RELEASE_INHERITED(nsSVGFEImageElement,nsSVGFEImageElementBase)
 
 DOMCI_NODE_DATA(SVGFEImageElement, nsSVGFEImageElement)
 
 NS_INTERFACE_TABLE_HEAD(nsSVGFEImageElement)
-  NS_NODE_INTERFACE_TABLE9(nsSVGFEImageElement, nsIDOMNode, nsIDOMElement,
+  NS_NODE_INTERFACE_TABLE8(nsSVGFEImageElement, nsIDOMNode, nsIDOMElement,
                            nsIDOMSVGElement,
                            nsIDOMSVGFilterPrimitiveStandardAttributes,
                            nsIDOMSVGFEImageElement, nsIDOMSVGURIReference,
-                           imgIDecoderObserver, nsIImageLoadingContent,
-                           imgIOnloadBlocker)
+                           imgIDecoderObserver, nsIImageLoadingContent)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFEImageElement)
 NS_INTERFACE_MAP_END_INHERITING(nsSVGFEImageElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGFEImageElement::nsSVGFEImageElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsSVGFEImageElementBase(aNodeInfo)
--- a/content/svg/content/src/nsSVGImageElement.cpp
+++ b/content/svg/content/src/nsSVGImageElement.cpp
@@ -67,21 +67,21 @@ NS_IMPL_NS_NEW_SVG_ELEMENT(Image)
 // nsISupports methods
 
 NS_IMPL_ADDREF_INHERITED(nsSVGImageElement,nsSVGImageElementBase)
 NS_IMPL_RELEASE_INHERITED(nsSVGImageElement,nsSVGImageElementBase)
 
 DOMCI_NODE_DATA(SVGImageElement, nsSVGImageElement)
 
 NS_INTERFACE_TABLE_HEAD(nsSVGImageElement)
-  NS_NODE_INTERFACE_TABLE9(nsSVGImageElement, nsIDOMNode, nsIDOMElement,
+  NS_NODE_INTERFACE_TABLE8(nsSVGImageElement, nsIDOMNode, nsIDOMElement,
                            nsIDOMSVGElement, nsIDOMSVGTests,
                            nsIDOMSVGImageElement,
                            nsIDOMSVGURIReference, imgIDecoderObserver,
-                           nsIImageLoadingContent, imgIOnloadBlocker)
+                           nsIImageLoadingContent)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGImageElement)
 NS_INTERFACE_MAP_END_INHERITING(nsSVGImageElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsSVGImageElement::nsSVGImageElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsSVGImageElementBase(aNodeInfo)
--- a/image/public/Makefile.in
+++ b/image/public/Makefile.in
@@ -50,15 +50,14 @@ EXPORTS		= ImageErrors.h ImageLogging.h
 XPIDLSRCS	= \
 		imgICache.idl             \
 		imgIContainer.idl         \
 		imgIContainerDebug.idl    \
 		imgIContainerObserver.idl \
 		imgIDecoderObserver.idl   \
 		imgIEncoder.idl           \
 		imgILoader.idl            \
-		imgIOnloadBlocker.idl     \
 		imgIRequest.idl           \
 		imgITools.idl             \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
deleted file mode 100644
--- a/image/public/imgIOnloadBlocker.idl
+++ /dev/null
@@ -1,64 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2012
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Kyle Huey <me@kylehuey.com> (original author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-interface imgIRequest;
-
-[uuid(dc126d90-0ee0-4683-b942-2fa66e443abc)]
-interface imgIOnloadBlocker : nsISupports
-{
-    /**
-     * blockOnload
-     * Called when it is appropriate to block onload for the given imgIRequest.
-     *
-     * @param aRequest
-     *        The request that should block onload.
-     */
-    void blockOnload(in imgIRequest aRequest);
-
-    /**
-     * unblockOnload
-     * Called when it is appropriate to unblock onload for the given
-     * imgIRequest.
-     *
-     * @param aRequest
-     *        The request that should unblock onload.
-     */
-    void unblockOnload(in imgIRequest aRequest);
-};
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -112,17 +112,17 @@ NS_IMPL_ISUPPORTS8(imgRequest,
                    nsIChannelEventSink,
                    nsIInterfaceRequestor,
                    nsIAsyncVerifyRedirectCallback)
 
 imgRequest::imgRequest() : 
   mValidator(nsnull), mImageSniffers("image-sniffing-services"),
   mInnerWindowId(0), mCORSMode(imgIRequest::CORS_NONE),
   mDecodeRequested(false), mIsMultiPartChannel(false), mGotData(false),
-  mIsInCache(false), mBlockingOnload(false)
+  mIsInCache(false)
 {
   // Register our pref observers if we haven't yet.
   if (NS_UNLIKELY(!gInitializedPrefCaches)) {
     InitPrefCaches();
   }
 }
 
 imgRequest::~imgRequest()
@@ -307,28 +307,16 @@ void imgRequest::CancelAndAbort(nsresult
 
 void imgRequest::Cancel(nsresult aStatus)
 {
   /* The Cancel() method here should only be called by this class. */
 
   LOG_SCOPE(gImgLog, "imgRequest::Cancel");
 
   imgStatusTracker& statusTracker = GetStatusTracker();
-
-  if (mBlockingOnload) {
-    mBlockingOnload = false;
-
-    statusTracker.RecordUnblockOnload();
-
-    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
-    while (iter.HasMore()) {
-      statusTracker.SendUnblockOnload(iter.GetNext());
-    }
-  }
-
   statusTracker.RecordCancel();
 
   RemoveFromCache();
 
   if (mRequest && statusTracker.IsLoading())
     mRequest->Cancel(aStatus);
 }
 
@@ -552,34 +540,21 @@ NS_IMETHODIMP imgRequest::FrameChanged(i
 /* void onStartDecode (in imgIRequest request); */
 NS_IMETHODIMP imgRequest::OnStartDecode(imgIRequest *request)
 {
   LOG_SCOPE(gImgLog, "imgRequest::OnStartDecode");
   NS_ABORT_IF_FALSE(mImage,
                     "OnStartDecode callback before we've created our image");
 
 
-  imgStatusTracker& tracker = mImage->GetStatusTracker();
-  tracker.RecordStartDecode();
+  mImage->GetStatusTracker().RecordStartDecode();
 
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
-    tracker.SendStartDecode(iter.GetNext());
-  }
-
-  if (!mIsMultiPartChannel) {
-    MOZ_ASSERT(!mBlockingOnload);
-    mBlockingOnload = true;
-
-    tracker.RecordBlockOnload();
-
-    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
-    while (iter.HasMore()) {
-      tracker.SendBlockOnload(iter.GetNext());
-    }
+    mImage->GetStatusTracker().SendStartDecode(iter.GetNext());
   }
 
   /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
      indicates the beginning of a new decode.
      The cache entry's size therefore needs to be reset to 0 here.  If we do not do this,
      the code in imgRequest::OnStopFrame will continue to increase the data size cumulatively.
    */
   if (mCacheEntry)
@@ -656,69 +631,39 @@ NS_IMETHODIMP imgRequest::OnDataAvailabl
 /* void onStopFrame (in imgIRequest request, in unsigned long frame); */
 NS_IMETHODIMP imgRequest::OnStopFrame(imgIRequest *request,
                                       PRUint32 frame)
 {
   LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame");
   NS_ABORT_IF_FALSE(mImage,
                     "OnStopFrame callback before we've created our image");
 
-  imgStatusTracker& tracker = mImage->GetStatusTracker();
-  tracker.RecordStopFrame(frame);
+  mImage->GetStatusTracker().RecordStopFrame(frame);
 
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
-    tracker.SendStopFrame(iter.GetNext(), frame);
-  }
-
-  if (mBlockingOnload) {
-    mBlockingOnload = false;
-
-    tracker.RecordUnblockOnload();
-
-    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
-    while (iter.HasMore()) {
-      tracker.SendUnblockOnload(iter.GetNext());
-    }
+    mImage->GetStatusTracker().SendStopFrame(iter.GetNext(), frame);
   }
 
   return NS_OK;
 }
 
 /* void onStopContainer (in imgIRequest request, in imgIContainer image); */
 NS_IMETHODIMP imgRequest::OnStopContainer(imgIRequest *request,
                                           imgIContainer *image)
 {
   LOG_SCOPE(gImgLog, "imgRequest::OnStopContainer");
   NS_ABORT_IF_FALSE(mImage,
                     "OnDataContainer callback before we've created our image");
 
-  imgStatusTracker& tracker = mImage->GetStatusTracker();
-  tracker.RecordStopContainer(image);
+  mImage->GetStatusTracker().RecordStopContainer(image);
 
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
-    tracker.SendStopContainer(iter.GetNext(), image);
-  }
-
-  // This is really hacky. We need to handle the case where we start decoding,
-  // block onload, but then hit an error before we get to our first frame. In
-  // theory we would just hook in at OnStopDecode, but OnStopDecode is broken
-  // until we fix bug 505385. OnStopContainer is actually going away at that
-  // point. So for now we take advantage of the fact that OnStopContainer is
-  // always fired in the decoders at the same time as OnStopDecode.
-  if (mBlockingOnload) {
-    mBlockingOnload = false;
-
-    tracker.RecordUnblockOnload();
-
-    nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
-    while (iter.HasMore()) {
-      tracker.SendUnblockOnload(iter.GetNext());
-    }
+    mImage->GetStatusTracker().SendStopContainer(iter.GetNext(), image);
   }
 
   return NS_OK;
 }
 
 /* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */
 NS_IMETHODIMP imgRequest::OnStopDecode(imgIRequest *aRequest,
                                        nsresult aStatus,
--- a/image/src/imgRequest.h
+++ b/image/src/imgRequest.h
@@ -257,12 +257,11 @@ private:
 
   // Sometimes consumers want to do things before the image is ready. Let them,
   // and apply the action when the image becomes available.
   bool mDecodeRequested : 1;
 
   bool mIsMultiPartChannel : 1;
   bool mGotData : 1;
   bool mIsInCache : 1;
-  bool mBlockingOnload : 1;
 };
 
 #endif
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -33,17 +33,16 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "imgRequestProxy.h"
-#include "imgIOnloadBlocker.h"
 
 #include "nsIInputStream.h"
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
 #include "nsIMultiPartChannel.h"
 
 #include "nsString.h"
 #include "nsXPIDLString.h"
@@ -785,44 +784,16 @@ void imgRequestProxy::OnStopRequest(bool
     // everything.  Note that this can cancel us and other fun things
     // like that.  Don't add anything in this method after this point.
     imgIDecoderObserver* obs = mListener;
     mListenerIsStrongRef = false;
     NS_RELEASE(obs);
   }
 }
 
-void imgRequestProxy::BlockOnload()
-{
-#ifdef PR_LOGGING
-  nsCAutoString name;
-  GetName(name);
-  LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::BlockOnload", "name", name.get());
-#endif
-
-  nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
-  if (blocker) {
-    blocker->BlockOnload(this);
-  }
-}
-
-void imgRequestProxy::UnblockOnload()
-{
-#ifdef PR_LOGGING
-  nsCAutoString name;
-  GetName(name);
-  LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::UnblockOnload", "name", name.get());
-#endif
-
-  nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
-  if (blocker) {
-    blocker->UnblockOnload(this);
-  }
-}
-
 void imgRequestProxy::NullOutListener()
 {
   // If we have animation consumers, then they don't matter anymore
   if (mListener)
     ClearAnimationConsumers();
 
   if (mListenerIsStrongRef) {
     // Releasing could do weird reentery stuff, so just play it super-safe
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -181,20 +181,16 @@ protected:
   /* non-virtual imgIContainerObserver methods */
   void FrameChanged(imgIContainer *aContainer,
                     const nsIntRect *aDirtyRect);
 
   /* non-virtual sort-of-nsIRequestObserver methods */
   void OnStartRequest();
   void OnStopRequest(bool aLastPart);
 
-  /* non-virtual imgIOnloadBlocker methods */
-  void BlockOnload();
-  void UnblockOnload();
-
   /* Finish up canceling ourselves */
   void DoCancel(nsresult status);
 
   /* Do the proper refcount management to null out mListener */
   void NullOutListener();
 
   void DoRemoveFromLoadGroup() {
     RemoveFromLoadGroup(true);
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -228,20 +228,16 @@ imgStatusTracker::SyncNotify(imgRequestP
   // OnStartContainer
   if (mState & stateHasSize)
     proxy->OnStartContainer(mImage);
 
   // OnStartDecode
   if (mState & stateDecodeStarted)
     proxy->OnStartDecode();
 
-  // BlockOnload
-  if (mState & stateBlockingOnload)
-    proxy->BlockOnload();
-
   if (mImage) {
     PRInt16 imageType = mImage->GetType();
     // Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame)
     if (imageType == imgIContainer::TYPE_VECTOR ||
         static_cast<RasterImage*>(mImage)->GetNumFrames() > 0) {
 
       PRUint32 frame = (imageType == imgIContainer::TYPE_VECTOR) ?
         0 : static_cast<RasterImage*>(mImage)->GetCurrentFrameIndex();
@@ -515,17 +511,16 @@ imgStatusTracker::RecordStartRequest()
   // load/decode
   mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
   mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
   mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
   mState &= ~stateRequestStarted;
   mState &= ~stateDecodeStarted;
   mState &= ~stateDecodeStopped;
   mState &= ~stateRequestStopped;
-  mState &= ~stateBlockingOnload;
 
   mState |= stateRequestStarted;
 }
 
 void
 imgStatusTracker::SendStartRequest(imgRequestProxy* aProxy)
 {
   if (!aProxy->NotificationsDeferred())
@@ -548,38 +543,8 @@ imgStatusTracker::SendStopRequest(imgReq
 {
   // See bug 505385 and imgRequest::OnStopDecode for more information on why
   // OnStopDecode is called with OnStopRequest.
   if (!aProxy->NotificationsDeferred()) {
     aProxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nsnull);
     aProxy->OnStopRequest(aLastPart);
   }
 }
-
-void
-imgStatusTracker::RecordBlockOnload()
-{
-  MOZ_ASSERT(!(mState & stateBlockingOnload));
-  mState |= stateBlockingOnload;
-}
-
-void
-imgStatusTracker::SendBlockOnload(imgRequestProxy* aProxy)
-{
-  if (!aProxy->NotificationsDeferred()) {
-    aProxy->BlockOnload();
-  }
-}
-
-void
-imgStatusTracker::RecordUnblockOnload()
-{
-  MOZ_ASSERT(mState & stateBlockingOnload);
-  mState &= ~stateBlockingOnload;
-}
-
-void
-imgStatusTracker::SendUnblockOnload(imgRequestProxy* aProxy)
-{
-  if (!aProxy->NotificationsDeferred()) {
-    aProxy->UnblockOnload();
-  }
-}
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -59,18 +59,17 @@ class Image;
 #include "nscore.h"
 
 enum {
   stateRequestStarted    = PR_BIT(0),
   stateHasSize           = PR_BIT(1),
   stateDecodeStarted     = PR_BIT(2),
   stateDecodeStopped     = PR_BIT(3),
   stateFrameStopped      = PR_BIT(4),
-  stateRequestStopped    = PR_BIT(5),
-  stateBlockingOnload    = PR_BIT(6)
+  stateRequestStopped    = PR_BIT(5)
 };
 
 /*
  * The image status tracker is a class that encapsulates all the loading and
  * decoding status about an Image, and makes it possible to send notifications
  * to imgRequestProxys, both synchronously (i.e., the status now) and
  * asynchronously (the status later).
  *
@@ -170,25 +169,16 @@ public:
                         const nsIntRect* aDirtyRect);
 
   /* non-virtual sort-of-nsIRequestObserver methods */
   void RecordStartRequest();
   void SendStartRequest(imgRequestProxy* aProxy);
   void RecordStopRequest(bool aLastPart, nsresult aStatus);
   void SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus);
 
-  /* non-virtual imgIOnloadBlocker methods */
-  // NB: If UnblockOnload is sent, and then we are asked to replay the
-  // notifications, we will not send a BlockOnload/UnblockOnload pair.  This
-  // is different from all the other notifications.
-  void RecordBlockOnload();
-  void SendBlockOnload(imgRequestProxy* aProxy);
-  void RecordUnblockOnload();
-  void SendUnblockOnload(imgRequestProxy* aProxy);
-
 private:
   friend class imgStatusNotifyRunnable;
   friend class imgRequestNotifyRunnable;
 
   nsCOMPtr<nsIRunnable> mRequestRunnable;
 
   // A weak pointer to the Image, because it owns us, and we
   // can't create a cycle.
--- a/image/test/unit/image_load_helpers.js
+++ b/image/test/unit/image_load_helpers.js
@@ -70,17 +70,17 @@ function ImageListener(start_callback, s
     do_check_false(this.synchronous);
 
     // onStopDecode must always be called before, and with, onStopRequest. See
     // imgRequest::OnStopDecode for more information.
     do_check_true(!!(this.state & STOP_DECODE));
 
     // We have to cancel the request when we're done with it to break any
     // reference loops!
-    aRequest.cancelAndForgetObserver(0);
+    aRequest.cancel(0);
 
     this.state |= STOP_REQUEST;
 
     if (this.stop_callback)
       this.stop_callback(this, aRequest);
   }
 
   // Initialize the synchronous flag to true to start. This must be set to
--- a/layout/base/Makefile.in
+++ b/layout/base/Makefile.in
@@ -102,16 +102,17 @@ CPPSRCS		= \
 		nsCaret.cpp \
 		nsChildIterator.cpp \
 		nsCounterManager.cpp \
 		nsDisplayList.cpp \
 		nsDocumentViewer.cpp \
 		nsFrameManager.cpp \
 		nsFrameTraversal.cpp \
 		nsGenConList.cpp \
+		nsImageLoader.cpp \
 		nsLayoutDebugger.cpp \
 		nsLayoutHistoryState.cpp \
 		nsLayoutUtils.cpp \
 		nsPresArena.cpp \
 		nsPresContext.cpp \
 		nsPresShell.cpp \
 		nsQuoteList.cpp \
 		nsRefreshDriver.cpp \
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -79,20 +79,18 @@
 #include "nsCSSFrameConstructor.h"
 #include "nsCSSProps.h"
 #include "nsContentUtils.h"
 #include "nsSVGEffects.h"
 #include "nsSVGIntegrationUtils.h"
 #include "gfxDrawable.h"
 
 #include "nsCSSRenderingBorders.h"
-#include "mozilla/css/ImageLoader.h"
 
 using namespace mozilla;
-using namespace mozilla::css;
 
 /**
  * This is a small wrapper class to encapsulate image drawing that can draw an
  * nsStyleImage image, which may internally be a real image, a sub image, or a
  * CSS gradient.
  *
  * @note Always call the member functions in the order of PrepareImage(),
  * ComputeSize(), and Draw().
@@ -2416,30 +2414,20 @@ nsCSSRendering::PaintBackgroundWithSC(ns
   // this far.)
   if (!drawBackgroundImage) {
     if (!isCanvasFrame) {
       DrawBackgroundColor(clipState, ctx, haveRoundedCorners, appUnitsPerPixel);
     }
     return;
   }
 
-  // Ensure we get invalidated for loads of the image.  If this is not the
-  // frame's normal style context this is the only code that knows about the
+  // Ensure we get invalidated for loads of the image.  We need to do
+  // this here because this might be the only code that knows about the
   // association of the style data with the frame.
-  if (aBackgroundSC != aForFrame->GetStyleContext()) {
-    ImageLoader* loader = aPresContext->Document()->StyleImageLoader();
-    
-    NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
-      if (bg->mLayers[i].mImage.GetType() == eStyleImageType_Image) {
-        imgIRequest *image = bg->mLayers[i].mImage.GetImageData();
-
-        loader->AssociateRequestToFrame(image, aForFrame);
-      }
-    }
-  }
+  aPresContext->SetupBackgroundImageLoaders(aForFrame, bg);
 
   // We can skip painting the background color if a background image is opaque.
   if (drawBackgroundColor &&
       bg->BottomLayer().mRepeat.mXRepeat == NS_STYLE_BG_REPEAT_REPEAT &&
       bg->BottomLayer().mRepeat.mYRepeat == NS_STYLE_BG_REPEAT_REPEAT &&
       bg->BottomLayer().mImage.IsOpaque())
     drawBackgroundColor = false;
 
@@ -2739,21 +2727,19 @@ DrawBorderImage(nsPresContext*       aPr
     return;
 
   // Ensure we get invalidated for loads and animations of the image.
   // We need to do this here because this might be the only code that
   // knows about the association of the style data with the frame.
   // XXX We shouldn't really... since if anybody is passing in a
   // different style, they'll potentially have the wrong size for the
   // border too.
+  aPresContext->SetupBorderImageLoaders(aForFrame, &aStyleBorder);
+
   imgIRequest *req = aStyleBorder.GetBorderImage();
-  ImageLoader* loader = aPresContext->Document()->StyleImageLoader();
-
-  // If this fails there's not much we can do ...
-  loader->AssociateRequestToFrame(req, aForFrame);
 
   // Get the actual image.
 
   nsCOMPtr<imgIContainer> imgContainer;
   req->GetImage(getter_AddRefs(imgContainer));
   NS_ASSERTION(imgContainer, "no image to draw");
 
   nsIntSize imageSize;
new file mode 100644
--- /dev/null
+++ b/layout/base/nsImageLoader.cpp
@@ -0,0 +1,284 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Stuart Parmenter <pavlov@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* class to notify frames of background image loads */
+
+#include "nsImageLoader.h"
+
+#include "imgILoader.h"
+
+#include "nsIURI.h"
+#include "nsILoadGroup.h"
+#include "nsNetUtil.h"
+
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
+#include "nsIFrame.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+
+#include "imgIContainer.h"
+
+#include "nsStyleContext.h"
+#include "nsGkAtoms.h"
+#include "nsLayoutUtils.h"
+
+// Paint forcing
+#include "prenv.h"
+
+NS_IMPL_ISUPPORTS2(nsImageLoader, imgIDecoderObserver, imgIContainerObserver)
+
+nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
+                             nsImageLoader *aNextLoader)
+  : mFrame(aFrame),
+    mActions(aActions),
+    mNextLoader(aNextLoader),
+    mRequestRegistered(false)
+{
+}
+
+nsImageLoader::~nsImageLoader()
+{
+  Destroy();
+}
+
+/* static */ already_AddRefed<nsImageLoader>
+nsImageLoader::Create(nsIFrame *aFrame, imgIRequest *aRequest, 
+                      PRUint32 aActions, nsImageLoader *aNextLoader)
+{
+  nsRefPtr<nsImageLoader> loader =
+    new nsImageLoader(aFrame, aActions, aNextLoader);
+
+  loader->Load(aRequest);
+
+  return loader.forget();
+}
+
+void
+nsImageLoader::Destroy()
+{
+  // Destroy the chain with only one level of recursion.
+  nsRefPtr<nsImageLoader> list = mNextLoader;
+  mNextLoader = nsnull;
+  while (list) {
+    nsRefPtr<nsImageLoader> todestroy = list;
+    list = todestroy->mNextLoader;
+    todestroy->mNextLoader = nsnull;
+    todestroy->Destroy();
+  }
+
+  if (mRequest && mFrame) {
+    nsLayoutUtils::DeregisterImageRequest(mFrame->PresContext(), mRequest,
+                                          &mRequestRegistered);
+  }
+
+  mFrame = nsnull;
+
+  if (mRequest) {
+    mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
+  }
+
+  mRequest = nsnull;
+}
+
+nsresult
+nsImageLoader::Load(imgIRequest *aImage)
+{
+  NS_ASSERTION(!mRequest, "can't reuse image loaders");
+  NS_ASSERTION(mFrame, "not initialized");
+  NS_ASSERTION(aImage, "must have non-null image");
+
+  if (!mFrame)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  if (!aImage)
+    return NS_ERROR_FAILURE;
+
+  // Deregister mRequest from the refresh driver, since it is no longer
+  // going to be managed by this nsImageLoader.
+  nsPresContext* presContext = mFrame->PresContext();
+
+  nsLayoutUtils::DeregisterImageRequest(presContext, mRequest,
+                                        &mRequestRegistered);
+
+  // Make sure to clone into a temporary, then set mRequest, since
+  // cloning may notify and we don't want to trigger paints from this
+  // code.
+  nsCOMPtr<imgIRequest> newRequest;
+  nsresult rv = aImage->Clone(this, getter_AddRefs(newRequest));
+  mRequest.swap(newRequest);
+
+  if (mRequest) {
+    nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mRequest,
+                                                  &mRequestRegistered);
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP nsImageLoader::OnStartContainer(imgIRequest *aRequest,
+                                              imgIContainer *aImage)
+{
+  NS_ABORT_IF_FALSE(aImage, "Who's calling us then?");
+
+  /* Get requested animation policy from the pres context:
+   *   normal = 0
+   *   one frame = 1
+   *   one loop = 2
+   */
+  aImage->SetAnimationMode(mFrame->PresContext()->ImageAnimationMode());
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsImageLoader::OnStopFrame(imgIRequest *aRequest,
+                                         PRUint32 aFrame)
+{
+  if (!mFrame)
+    return NS_ERROR_FAILURE;
+
+  if (!mRequest) {
+    // We're in the middle of a paint anyway
+    return NS_OK;
+  }
+
+  // Take requested actions
+  if (mActions & ACTION_REDRAW_ON_DECODE) {
+    DoRedraw(nsnull);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsImageLoader::OnImageIsAnimated(imgIRequest *aRequest)
+{
+  // Register with the refresh driver now that we are aware that
+  // we are animated.
+  nsLayoutUtils::RegisterImageRequest(mFrame->PresContext(),
+                                      aRequest, &mRequestRegistered);
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsImageLoader::OnStopRequest(imgIRequest *aRequest,
+                                           bool aLastPart)
+{
+  if (!mFrame)
+    return NS_ERROR_FAILURE;
+
+  if (!mRequest) {
+    // We're in the middle of a paint anyway
+    return NS_OK;
+  }
+
+  // Take requested actions
+  if (mActions & ACTION_REDRAW_ON_LOAD) {
+    DoRedraw(nsnull);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsImageLoader::FrameChanged(imgIRequest *aRequest,
+                                          imgIContainer *aContainer,
+                                          const nsIntRect *aDirtyRect)
+{
+  if (!mFrame)
+    return NS_ERROR_FAILURE;
+
+  if (!mRequest) {
+    // We're in the middle of a paint anyway
+    return NS_OK;
+  }
+
+  NS_ASSERTION(aRequest == mRequest, "This is a neat trick.");
+
+  nsRect r = aDirtyRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect()) ?
+    nsRect(nsPoint(0, 0), mFrame->GetSize()) :
+    aDirtyRect->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
+
+  DoRedraw(&r);
+
+  return NS_OK;
+}
+
+void
+nsImageLoader::DoRedraw(const nsRect* aDamageRect)
+{
+  // NOTE: It is not sufficient to invalidate only the size of the image:
+  //       the image may be tiled! 
+  //       The best option is to call into the frame, however lacking this
+  //       we have to at least invalidate the frame's bounds, hence
+  //       as long as we have a frame we'll use its size.
+  //
+
+  // Invalidate the entire frame
+  // XXX We really only need to invalidate the client area of the frame...    
+
+  nsRect bounds(nsPoint(0, 0), mFrame->GetSize());
+
+  if (mFrame->GetType() == nsGkAtoms::canvasFrame) {
+    // The canvas's background covers the whole viewport.
+    bounds = mFrame->GetVisualOverflowRect();
+  }
+
+  // XXX this should be ok, but there is some crappy ass bug causing it not to work
+  // XXX seems related to the "body fixup rule" dealing with the canvas and body frames...
+#if 0
+  // Invalidate the entire frame only if the frame has a tiled background
+  // image, otherwise just invalidate the intersection of the frame's bounds
+  // with the damaged rect.
+  nsStyleContext* styleContext;
+  mFrame->GetStyleContext(&styleContext);
+  const nsStyleBackground* bg = styleContext->GetStyleBackground();
+
+  if ((bg->mBackgroundFlags & NS_STYLE_BG_IMAGE_NONE) ||
+      (bg->mBackgroundRepeat == NS_STYLE_BG_REPEAT_OFF)) {
+    // The frame does not have a background image so we are free
+    // to invalidate only the intersection of the damage rect and
+    // the frame's bounds.
+
+    if (aDamageRect) {
+      bounds.IntersectRect(*aDamageRect, bounds);
+    }
+  }
+
+#endif
+
+  if (mFrame->GetStyleVisibility()->IsVisible()) {
+    mFrame->Invalidate(bounds);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/layout/base/nsImageLoader.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Stuart Parmenter <pavlov@netscape.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* class to notify frames of background and border image loads */
+
+#include "nsStubImageDecoderObserver.h"
+
+class nsIFrame;
+class nsIURI;
+
+#include "imgIRequest.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+
+/**
+ * Image loaders pass notifications for background and border image
+ * loading and animation on to the frames.
+ *
+ * Each frame's image loaders form a linked list.
+ */
+class nsImageLoader : public nsStubImageDecoderObserver
+{
+private:
+  nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
+                nsImageLoader *aNextLoader);
+  virtual ~nsImageLoader();
+
+public:
+  /*
+   * Flags to specify actions that can be taken for the image at various
+   * times. Reflows always occur before redraws. "Decode" refers to one
+   * frame being available, whereas "load" refers to all the data being loaded
+   * from the network.
+   */
+  enum {
+    ACTION_REDRAW_ON_DECODE = 0x01,
+    ACTION_REDRAW_ON_LOAD   = 0x02,
+  };
+  static already_AddRefed<nsImageLoader>
+    Create(nsIFrame *aFrame, imgIRequest *aRequest,
+           PRUint32 aActions, nsImageLoader *aNextLoader);
+
+  NS_DECL_ISUPPORTS
+
+  // imgIDecoderObserver (override nsStubImageDecoderObserver)
+  NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
+  NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
+  NS_IMETHOD OnStopRequest(imgIRequest *aRequest, bool aLastPart);
+  NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest);
+  // Do not override OnDataAvailable since background images are not
+  // displayed incrementally; they are displayed after the entire image
+  // has been loaded.
+  // Note: Images referenced by the <img> element are displayed
+  // incrementally in nsImageFrame.cpp.
+
+  // imgIContainerObserver (override nsStubImageDecoderObserver)
+  NS_IMETHOD FrameChanged(imgIRequest *aRequest,
+                          imgIContainer *aContainer,
+                          const nsIntRect *aDirtyRect);
+
+  void Destroy();
+
+  imgIRequest *GetRequest() { return mRequest; }
+  nsImageLoader *GetNextLoader() { return mNextLoader; }
+
+private:
+  nsresult Load(imgIRequest *aImage);
+  /* if aDamageRect is nsnull, the whole frame is redrawn. */
+  void DoRedraw(const nsRect* aDamageRect);
+
+  nsIFrame *mFrame;
+  nsCOMPtr<imgIRequest> mRequest;
+  PRUint32 mActions;
+  nsRefPtr<nsImageLoader> mNextLoader;
+
+  // This is a boolean flag indicating whether or not the current image request
+  // has been registered with the refresh driver.
+  bool mRequestRegistered;
+};
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -43,16 +43,17 @@
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsILinkHandler.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShell.h"
 #include "nsIContentViewer.h"
 #include "nsPIDOMWindow.h"
 #include "nsStyleSet.h"
+#include "nsImageLoader.h"
 #include "nsIContent.h"
 #include "nsIFrame.h"
 #include "nsIURL.h"
 #include "nsIDocument.h"
 #include "nsStyleContext.h"
 #include "mozilla/LookAndFeel.h"
 #include "nsIComponentManager.h"
 #include "nsIURIContentListener.h"
@@ -91,17 +92,16 @@
 #include "nsObjectFrame.h"
 #include "nsTransitionManager.h"
 #include "nsAnimationManager.h"
 #include "mozilla/dom/Element.h"
 #include "nsIFrameMessageManager.h"
 #include "FrameLayerBuilder.h"
 #include "nsDOMMediaQueryList.h"
 #include "nsSMILAnimationController.h"
-#include "mozilla/css/ImageLoader.h"
 
 #ifdef IBMBIDI
 #include "nsBidiPresUtils.h"
 #endif // IBMBIDI
 
 #include "nsContentUtils.h"
 #include "nsPIWindowRoot.h"
 #include "mozilla/Preferences.h"
@@ -189,16 +189,24 @@ IsVisualCharset(const nsCString& aCharse
     return true; // visual text type
   }
   else {
     return false; // logical text type
   }
 }
 #endif // IBMBIDI
 
+
+static PLDHashOperator
+destroy_loads(nsIFrame* aKey, nsRefPtr<nsImageLoader>& aData, void* closure)
+{
+  aData->Destroy();
+  return PL_DHASH_NEXT;
+}
+
 #include "nsContentCID.h"
 
   // NOTE! nsPresContext::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 
 nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
   : mType(aType), mDocument(aDocument), mMinFontSize(0),
     mTextZoom(1.0), mFullZoom(1.0), mPageSize(-1, -1), mPPScale(1.0f),
@@ -330,22 +338,38 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresCon
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
    NS_INTERFACE_MAP_ENTRY(nsISupports)
    NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPresContext)
 
+static PLDHashOperator
+TraverseImageLoader(nsIFrame* aKey, nsRefPtr<nsImageLoader>& aData,
+                    void* aClosure)
+{
+  nsCycleCollectionTraversalCallback *cb =
+    static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mImageLoaders[i] item");
+  cb->NoteXPCOMChild(aData);
+
+  return PL_DHASH_NEXT;
+}
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mEventManager, nsIObserver);
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
 
+  for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
+    tmp->mImageLoaders[i].Enumerate(TraverseImageLoader, &cb);
+
   // We own only the items in mDOMMediaQueryLists that have listeners;
   // this reference is managed by their AddListener and RemoveListener
   // methods.
   for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
        l != &tmp->mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
     nsDOMMediaQueryList *mql = static_cast<nsDOMMediaQueryList*>(l);
     if (mql->HasListeners()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
@@ -924,16 +948,20 @@ nsPresContext::Init(nsDeviceContext* aDe
 
   mDeviceContext = aDeviceContext;
   NS_ADDREF(mDeviceContext);
 
   if (mDeviceContext->SetPixelScale(mFullZoom))
     mDeviceContext->FlushFontCache();
   mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
 
+  for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
+    if (!mImageLoaders[i].Init())
+      return NS_ERROR_OUT_OF_MEMORY;
+
   mEventManager = new nsEventStateManager();
   NS_ADDREF(mEventManager);
 
   mTransitionManager = new nsTransitionManager(this);
 
   mAnimationManager = new nsAnimationManager(this);
 
   if (mDocument->GetDisplayDocument()) {
@@ -1098,16 +1126,28 @@ nsPresContext::SetShell(nsIPresShell* aS
       // Have to cancel our plugin geometry timer, because the
       // callback for that depends on a non-null presshell.
       static_cast<nsRootPresContext*>(this)->CancelUpdatePluginGeometryTimer();
     }
   }
 }
 
 void
+nsPresContext::DestroyImageLoaders()
+{
+  // Destroy image loaders. This is important to do when frames are being
+  // destroyed because imageloaders can have pointers to frames and we don't
+  // want those pointers to outlive the destruction of the frame arena.
+  for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) {
+    mImageLoaders[i].Enumerate(destroy_loads, nsnull);
+    mImageLoaders[i].Clear();
+  }
+}
+
+void
 nsPresContext::DoChangeCharSet(const nsCString& aCharSet)
 {
   UpdateCharSet(aCharSet);
   mDeviceContext->FlushFontCache();
   RebuildAllStyleData(NS_STYLE_HINT_REFLOW);
 }
 
 void
@@ -1207,16 +1247,28 @@ static void SetImgAnimModeOnImgReq(imgIR
     nsCOMPtr<imgIContainer> imgCon;
     aImgReq->GetImage(getter_AddRefs(imgCon));
     if (imgCon) {
       imgCon->SetAnimationMode(aMode);
     }
   }
 }
 
+ // Enumeration call back for HashTable
+static PLDHashOperator
+set_animation_mode(nsIFrame* aKey, nsRefPtr<nsImageLoader>& aData, void* closure)
+{
+  for (nsImageLoader *loader = aData; loader;
+       loader = loader->GetNextLoader()) {
+    imgIRequest* imgReq = loader->GetRequest();
+    SetImgAnimModeOnImgReq(imgReq, (PRUint16)NS_PTR_TO_INT32(closure));
+  }
+  return PL_DHASH_NEXT;
+}
+
 // IMPORTANT: Assumption is that all images for a Presentation 
 // have the same Animation Mode (pavlov said this was OK)
 //
 // Walks content and set the animation mode
 // this is a way to turn on/off image animations
 void nsPresContext::SetImgAnimations(nsIContent *aParent, PRUint16 aMode)
 {
   nsCOMPtr<nsIImageLoadingContent> imgContent(do_QueryInterface(aParent));
@@ -1261,23 +1313,25 @@ nsPresContext::SetImageAnimationModeInte
   NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
                aMode == imgIContainer::kDontAnimMode ||
                aMode == imgIContainer::kLoopOnceAnimMode, "Wrong Animation Mode is being set!");
 
   // Image animation mode cannot be changed when rendering to a printer.
   if (!IsDynamic())
     return;
 
+  // Set the mode on the image loaders.
+  for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
+    mImageLoaders[i].Enumerate(set_animation_mode, NS_INT32_TO_PTR(aMode));
+
   // Now walk the content tree and set the animation mode 
   // on all the images.
   if (mShell != nsnull) {
     nsIDocument *doc = mShell->GetDocument();
     if (doc) {
-      doc->StyleImageLoader()->SetAnimationMode(aMode);
-
       Element *rootElement = doc->GetRootElement();
       if (rootElement) {
         SetImgAnimations(rootElement, aMode);
       }
       SetSMILAnimations(doc, aMode, mImageAnimationMode);
     }
   }
 
@@ -1354,16 +1408,78 @@ nsPresContext::SetFullZoom(float aZoom)
   AppUnitsPerDevPixelChanged();
 
   mSupressResizeReflow = false;
 
   mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
 }
 
 void
+nsPresContext::SetImageLoaders(nsIFrame* aTargetFrame,
+                               ImageLoadType aType,
+                               nsImageLoader* aImageLoaders)
+{
+  NS_ASSERTION(mShell || !aImageLoaders,
+               "Shouldn't add new image loader after the shell is gone");
+
+  nsRefPtr<nsImageLoader> oldLoaders;
+  mImageLoaders[aType].Get(aTargetFrame, getter_AddRefs(oldLoaders));
+
+  if (aImageLoaders) {
+    mImageLoaders[aType].Put(aTargetFrame, aImageLoaders);
+  } else if (oldLoaders) {
+    mImageLoaders[aType].Remove(aTargetFrame);
+  }
+
+  if (oldLoaders)
+    oldLoaders->Destroy();
+}
+
+void
+nsPresContext::SetupBackgroundImageLoaders(nsIFrame* aFrame,
+                                     const nsStyleBackground* aStyleBackground)
+{
+  nsRefPtr<nsImageLoader> loaders;
+  NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, aStyleBackground) {
+    if (aStyleBackground->mLayers[i].mImage.GetType() == eStyleImageType_Image) {
+      PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_DECODE;
+      imgIRequest *image = aStyleBackground->mLayers[i].mImage.GetImageData();
+      loaders = nsImageLoader::Create(aFrame, image, actions, loaders);
+    }
+  }
+  SetImageLoaders(aFrame, BACKGROUND_IMAGE, loaders);
+}
+
+void
+nsPresContext::SetupBorderImageLoaders(nsIFrame* aFrame,
+                                       const nsStyleBorder* aStyleBorder)
+{
+  // We get called the first time we try to draw a border-image, and
+  // also when the border image changes (including when it changes from
+  // non-null to null).
+  imgIRequest *borderImage = aStyleBorder->GetBorderImage();
+  if (!borderImage) {
+    SetImageLoaders(aFrame, BORDER_IMAGE, nsnull);
+    return;
+  }
+
+  PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_LOAD;
+  nsRefPtr<nsImageLoader> loader =
+    nsImageLoader::Create(aFrame, borderImage, actions, nsnull);
+  SetImageLoaders(aFrame, BORDER_IMAGE, loader);
+}
+
+void
+nsPresContext::StopImagesFor(nsIFrame* aTargetFrame)
+{
+  for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
+    SetImageLoaders(aTargetFrame, ImageLoadType(i), nsnull);
+}
+
+void
 nsPresContext::SetContainer(nsISupports* aHandler)
 {
   mContainer = do_GetWeakReference(aHandler);
   InvalidateIsChromeCache();
   if (mContainer) {
     GetDocumentColorPreferences();
   }
 }
@@ -2059,18 +2175,16 @@ NotifyDidPaintSubdocumentCallback(nsIDoc
     }
   }
   return true;
 }
 
 void
 nsPresContext::NotifyDidPaintForSubtree()
 {
-  Document()->StyleImageLoader()->NotifyPaint();
-
   if (!mFireAfterPaintEvents)
     return;
   mFireAfterPaintEvents = false;
 
   if (IsRoot()) {
     static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer();
   }
 
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -69,16 +69,17 @@
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "nsIWidget.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIContent.h"
 #include "prclist.h"
 
+class nsImageLoader;
 #ifdef IBMBIDI
 class nsBidiPresUtils;
 #endif // IBMBIDI
 
 struct nsRect;
 
 class imgIRequest;
 
@@ -378,16 +379,59 @@ public:
   const nscolor BodyTextColor() const { return mBodyTextColor; }
   void SetBodyTextColor(nscolor aColor) { mBodyTextColor = aColor; }
 
   bool GetUseFocusColors() const { return mUseFocusColors; }
   PRUint8 FocusRingWidth() const { return mFocusRingWidth; }
   bool GetFocusRingOnAnything() const { return mFocusRingOnAnything; }
   PRUint8 GetFocusRingStyle() const { return mFocusRingStyle; }
 
+  /**
+   * The types of image load types that the pres context needs image
+   * loaders to track invalidation for.
+   */
+  enum ImageLoadType {
+    BACKGROUND_IMAGE,
+    BORDER_IMAGE,
+    IMAGE_LOAD_TYPE_COUNT
+  };
+
+  /**
+   * Set the list of image loaders that track invalidation for a
+   * specific frame and type of image.  This list will replace any
+   * previous list for that frame and image type (and null will remove
+   * any previous list).
+   */
+  NS_HIDDEN_(void) SetImageLoaders(nsIFrame* aTargetFrame,
+                                   ImageLoadType aType,
+                                   nsImageLoader* aImageLoaders);
+
+  /**
+   * Make an appropriate SetImageLoaders call (including potentially
+   * with null aImageLoaders) given that aFrame draws its background
+   * based on aStyleBackground.
+   */
+  NS_HIDDEN_(void) SetupBackgroundImageLoaders(nsIFrame* aFrame,
+                                               const nsStyleBackground*
+                                                 aStyleBackground);
+
+  /**
+   * Make an appropriate SetImageLoaders call (including potentially
+   * with null aImageLoaders) given that aFrame draws its border
+   * based on aStyleBorder.
+   */
+  NS_HIDDEN_(void) SetupBorderImageLoaders(nsIFrame* aFrame,
+                                           const nsStyleBorder* aStyleBorder);
+
+  /**
+   * This method is called when a frame is being destroyed to
+   * ensure that the image loads get disassociated from the prescontext
+   */
+  NS_HIDDEN_(void) StopImagesFor(nsIFrame* aTargetFrame);
+
   NS_HIDDEN_(void) SetContainer(nsISupports* aContainer);
 
   virtual NS_HIDDEN_(already_AddRefed<nsISupports>) GetContainerExternal() const;
   NS_HIDDEN_(already_AddRefed<nsISupports>) GetContainerInternal() const;
 #ifdef _IMPL_NS_LAYOUT
   already_AddRefed<nsISupports> GetContainer() const
   { return GetContainerInternal(); }
 #else
@@ -904,16 +948,18 @@ public:
   }
 
   void NotifyDestroyingFrame(nsIFrame* aFrame)
   {
     PropertyTable()->DeleteAllFor(aFrame);
   }
   inline void ForgetUpdatePluginGeometryFrame(nsIFrame* aFrame);
 
+  void DestroyImageLoaders();
+
   bool GetContainsUpdatePluginGeometryFrame()
   {
     return mContainsUpdatePluginGeometryFrame;
   }
 
   void SetContainsUpdatePluginGeometryFrame(bool aValue)
   {
     mContainsUpdatePluginGeometryFrame = aValue;
@@ -1080,16 +1126,20 @@ public:
   nsIFrame*             mCurrentInflationContainer; // [WEAK]
 
   // The content-rect width of mCurrentInflationContainer.  If
   // mCurrentInflationContainer is currently in reflow, this is its new
   // width, which is not yet set on its rect.
   nscoord               mCurrentInflationContainerWidth;
 
 protected:
+
+  nsRefPtrHashtable<nsPtrHashKey<nsIFrame>, nsImageLoader>
+                        mImageLoaders[IMAGE_LOAD_TYPE_COUNT];
+
   nsWeakPtr             mContainer;
 
   PRCList               mDOMMediaQueryLists;
 
   PRInt32               mMinFontSize;   // Min font size, defaults to 0
   float                 mTextZoom;      // Text zoom, defaults to 1.0
   float                 mFullZoom;      // Page zoom, defaults to 1.0
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -149,17 +149,16 @@
 #include "nsThreadUtils.h"
 #include "nsStyleSheetService.h"
 #include "gfxImageSurface.h"
 #include "gfxContext.h"
 #ifdef MOZ_MEDIA
 #include "nsHTMLMediaElement.h"
 #endif
 #include "nsSMILAnimationController.h"
-#include "mozilla/css/ImageLoader.h"
 
 #include "nsRefreshDriver.h"
 
 // Drag & Drop, Clipboard
 #include "nsWidgetsCID.h"
 #include "nsIClipboard.h"
 #include "nsIClipboardHelper.h"
 #include "nsIDocShellTreeItem.h"
@@ -2108,33 +2107,34 @@ PresShell::FireResizeEvent()
     nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status);
     mInResize = false;
   }
 }
 
 void
 PresShell::SetIgnoreFrameDestruction(bool aIgnore)
 {
-  if (mDocument) {
-    // We need to tell the ImageLoader to drop all its references to frames
-    // because they're about to go away and it won't get notifications of that.
-    mDocument->StyleImageLoader()->ClearAll();
+  if (mPresContext) {
+    // We need to destroy the image loaders first, as they won't be
+    // notified when frames are destroyed once this setting takes effect.
+    // (See bug 673984)
+    mPresContext->DestroyImageLoaders();
   }
   mIgnoreFrameDestruction = aIgnore;
 }
 
 void
 PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
 {
   NS_TIME_FUNCTION_MIN(1.0);
 
   mPresContext->ForgetUpdatePluginGeometryFrame(aFrame);
 
   if (!mIgnoreFrameDestruction) {
-    mDocument->StyleImageLoader()->DropRequestsForFrame(aFrame);
+    mPresContext->StopImagesFor(aFrame);
 
     mFrameConstructor->NotifyDestroyingFrame(aFrame);
 
     for (PRInt32 idx = mDirtyRoots.Length(); idx; ) {
       --idx;
       if (mDirtyRoots[idx] == aFrame) {
         mDirtyRoots.RemoveElementAt(idx);
       }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -124,22 +124,20 @@
 
 #include "gfxContext.h"
 #include "nsRenderingContext.h"
 #include "CSSCalc.h"
 #include "nsAbsoluteContainingBlock.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
-#include "mozilla/css/ImageLoader.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
-using namespace mozilla::css;
 
 // Struct containing cached metrics for box-wrapped frames.
 struct nsBoxLayoutMetrics
 {
   nsSize mPrefSize;
   nsSize mMinSize;
   nsSize mMaxSize;
 
@@ -704,54 +702,35 @@ EqualImages(imgIRequest *aOldImage, imgI
   bool equal;
   return NS_SUCCEEDED(oldURI->Equals(newURI, &equal)) && equal;
 }
 
 // Subclass hook for style post processing
 /* virtual */ void
 nsFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
-  ImageLoader* imageLoader = PresContext()->Document()->StyleImageLoader();
-
   if (aOldStyleContext) {
     // If the old context had a background image image 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.)
     const nsStyleBackground *oldBG = aOldStyleContext->GetStyleBackground();
     const nsStyleBackground *newBG = GetStyleBackground();
     NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, oldBG) {
-      // If there is an image in oldBG that's not in newBG, drop it.
       if (i >= newBG->mImageCount ||
           oldBG->mLayers[i].mImage != newBG->mLayers[i].mImage) {
-        const nsStyleImage& oldImage = oldBG->mLayers[i].mImage;
-        if (oldImage.GetType() != eStyleImageType_Image) {
-          continue;
-        }
-
-        imageLoader->DisassociateRequestFromFrame(oldImage.GetImageData(),
-                                                  this);
-      }          
-    }
-
-    NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, newBG) {
-      // If there is an image in newBG that's not in oldBG, add it.
-      if (i >= oldBG->mImageCount ||
-          newBG->mLayers[i].mImage != oldBG->mLayers[i].mImage) {
-        const nsStyleImage& newImage = newBG->mLayers[i].mImage;
-        if (newImage.GetType() != eStyleImageType_Image) {
-          continue;
-        }
-
-        imageLoader->AssociateRequestToFrame(newImage.GetImageData(), this);
-      }          
+        // stop the image loading for the frame, the image has changed
+        PresContext()->SetImageLoaders(this,
+          nsPresContext::BACKGROUND_IMAGE, nsnull);
+        break;
+      }
     }
 
     // If we detect a change on margin, padding or border, we store the old
     // values on the frame itself between now and reflow, so if someone
     // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
     // can give an accurate answer.
     // We don't want to set the property if one already exists.
     FrameProperties props = Properties();
@@ -782,38 +761,32 @@ nsFrame::DidSetStyleContext(nsStyleConte
         props.Set(UsedBorderProperty(), new nsMargin(oldValue));
       }
     }
   }
 
   imgIRequest *oldBorderImage = aOldStyleContext
     ? aOldStyleContext->GetStyleBorder()->GetBorderImage()
     : nsnull;
-  imgIRequest *newBorderImage = GetStyleBorder()->GetBorderImage();
   // 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 GetActualBorder() 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
   // we'll never know that it's finished loading.  Likewise, we want to
   // do this for freshly-created frames to prevent a similar race if the
   // image loads between reflow (which can depend on whether the image
   // is loaded) and paint.  We also don't really care about any callers
   // who try to paint borders with a different style context, because
   // they won't have the correct size for the border either.
-  if (!EqualImages(oldBorderImage, newBorderImage)) {
+  if (!EqualImages(oldBorderImage, GetStyleBorder()->GetBorderImage())) {
     // stop and restart the image loading/notification
-    if (oldBorderImage) {
-      imageLoader->DisassociateRequestFromFrame(oldBorderImage, this);
-    }
-    if (newBorderImage) {
-      imageLoader->AssociateRequestToFrame(newBorderImage, this);
-    }
+    PresContext()->SetupBorderImageLoaders(this, GetStyleBorder());
   }
 
   // If the page contains markup that overrides text direction, and
   // does not contain any characters that would activate the Unicode
   // bidi algorithm, we need to call |SetBidiEnabled| on the pres
   // context before reflow starts.  See bug 115921.
   if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
     PresContext()->SetBidiEnabled();
deleted file mode 100644
--- a/layout/reftests/backgrounds/really-big-background-ref.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<!DOCTYPE html>
-<img src="really-big-background.png" style="width: 1600px; height: 1200px; position: absolute;">
deleted file mode 100644
--- a/layout/reftests/backgrounds/really-big-background.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<!DOCTYPE html>
-<img style="background-image: url(really-big-background.png); width: 1600px; height: 1200px; position: absolute;"></img>
deleted file mode 100644
index bd8705b8a5fbc1f2323662a1b6d9330c67dbd0c8..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/layout/reftests/backgrounds/reftest.list
+++ b/layout/reftests/backgrounds/reftest.list
@@ -122,11 +122,9 @@ fails == background-size-zoom-repeat.htm
 # -moz-default-background-color and -moz-default-color (bug 591341)
 == background-moz-default-background-color.html background-moz-default-background-color-ref.html
 
 == fixed-bg-with-transform-outside-viewport-1.html fixed-bg-with-transform-outside-viewport-ref.html
 
 HTTP == root-background-1.html root-background-ref.html
 HTTP != root-background-1.html about:blank
 
-== really-big-background.html really-big-background-ref.html
-
 == background-repeat-1-ref.html background-repeat-1.html
deleted file mode 100644
--- a/layout/style/ImageLoader.cpp
+++ /dev/null
@@ -1,474 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* A class that handles style system image loads (other image loads are handled
- * by the nodes in the content tree).
- */
-
-#include "mozilla/css/ImageLoader.h"
-#include "nsContentUtils.h"
-#include "nsLayoutUtils.h"
-#include "nsNetError.h"
-
-namespace mozilla {
-namespace css {
-
-/* static */ PLDHashOperator
-ImageLoader::SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue,
-                                        void* aClosure)
-{
-  imgIRequest* request = static_cast<imgIRequest*>(aKey);
-
-  PRUint16* mode = static_cast<PRUint16*>(aClosure);
-
-#ifdef DEBUG
-  {
-    nsCOMPtr<imgIRequest> debugRequest = do_QueryInterface(aKey);
-    NS_ASSERTION(debugRequest == request, "This is bad");
-  }
-#endif
-
-  nsCOMPtr<imgIContainer> container;
-  request->GetImage(getter_AddRefs(container));
-  if (!container) {
-    return PL_DHASH_NEXT;
-  }
-
-  // This can fail if the image is in error, and we don't care.
-  container->SetAnimationMode(*mode);
-
-  return PL_DHASH_NEXT;
-}
-
-nsresult
-ImageLoader::Init()
-{
-  MOZ_ASSERT(mDocument);
-
-  if (!mRequestToFrameMap.Init() ||
-      !mFrameToRequestMap.Init() ||
-      !mImages.Init()) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return NS_OK;
-}
-
-void
-ImageLoader::DropDocumentReference()
-{
-  ClearAll();
-  mDocument = nsnull;
-}
-
-void
-ImageLoader::AssociateRequestToFrame(imgIRequest* aRequest,
-                                     nsIFrame* aFrame)
-{
-  MOZ_ASSERT(mRequestToFrameMap.IsInitialized() &&
-             mFrameToRequestMap.IsInitialized() &&
-             mImages.IsInitialized());
-
-  nsCOMPtr<imgIDecoderObserver> observer;
-  aRequest->GetDecoderObserver(getter_AddRefs(observer));
-  if (!observer) {
-    // The request has already been canceled, so ignore it.  This is ok because
-    // we're not going to get any more notifications from a canceled request.
-    return;
-  }
-
-  MOZ_ASSERT(observer == this);
-
-  FrameSet* frameSet = nsnull;
-  if (mRequestToFrameMap.Get(aRequest, &frameSet)) {
-    NS_ASSERTION(frameSet, "This should never be null!");
-  }
-
-  if (!frameSet) {
-    nsAutoPtr<FrameSet> newFrameSet(new FrameSet());
-
-    bool result = mRequestToFrameMap.Put(aRequest, newFrameSet);
-    if (!result) {
-      return;
-    }
-
-    frameSet = newFrameSet.forget();
-  }
-
-  RequestSet* requestSet = nsnull;
-  if (mFrameToRequestMap.Get(aFrame, &requestSet)) {
-    NS_ASSERTION(requestSet, "This should never be null");
-  }
-
-  if (!requestSet) {
-    nsAutoPtr<RequestSet> newRequestSet(new RequestSet());
-
-    bool result = mFrameToRequestMap.Put(aFrame, newRequestSet);
-    if (!result) {
-      return;
-    }
-
-    requestSet = newRequestSet.forget();
-  }
-
-  // Add these to the sets, but only if they're not already there.
-  PRUint32 i;
-  if (!frameSet->GreatestIndexLtEq(aFrame, i)) {
-    frameSet->InsertElementAt(i, aFrame);
-  }
-  if (!requestSet->GreatestIndexLtEq(aRequest, i)) {
-    requestSet->InsertElementAt(i, aRequest);
-  }
-}
-
-void
-ImageLoader::MaybeRegisterCSSImage(nsCSSValue::Image* aImage)
-{
-  NS_ASSERTION(aImage, "This should never be null!");
-
-  bool found = false;
-  aImage->mRequests.GetWeak(mDocument, &found);
-  if (found) {
-    // This document already has a request.
-    return;
-  }
-
-  imgIRequest* canonicalRequest = aImage->mRequests.GetWeak(nsnull);
-  if (!canonicalRequest) {
-    // The image was blocked or something.
-    return;
-  }
-
-  nsCOMPtr<imgIRequest> request;
-
-  // Ignore errors here.  If cloning fails for some reason we'll put a null
-  // entry in the hash and we won't keep trying to clone.
-  mInClone = true;
-  canonicalRequest->Clone(this, getter_AddRefs(request));
-  mInClone = false;
-
-  aImage->mRequests.Put(mDocument, request);
-
-  AddImage(aImage);
-}
-
-void
-ImageLoader::DeregisterCSSImage(nsCSSValue::Image* aImage)
-{
-  RemoveImage(aImage);
-}
-
-void
-ImageLoader::DisassociateRequestFromFrame(imgIRequest* aRequest,
-                                          nsIFrame* aFrame)
-{
-  FrameSet* frameSet = nsnull;
-  RequestSet* requestSet = nsnull;
-
-  MOZ_ASSERT(mRequestToFrameMap.IsInitialized() &&
-             mFrameToRequestMap.IsInitialized() &&
-             mImages.IsInitialized());
-
-#ifdef DEBUG
-  {
-    nsCOMPtr<imgIDecoderObserver> observer;
-    aRequest->GetDecoderObserver(getter_AddRefs(observer));
-    MOZ_ASSERT(!observer || observer == this);
-  }
-#endif
-
-  mRequestToFrameMap.Get(aRequest, &frameSet);
-  mFrameToRequestMap.Get(aFrame, &requestSet);
-
-  if (frameSet) {
-    frameSet->RemoveElementSorted(aFrame);
-  }
-  if (requestSet) {
-    requestSet->RemoveElementSorted(aRequest);
-  }
-
-  if (frameSet && !frameSet->Length()) {
-    mRequestToFrameMap.Remove(aRequest);
-
-    nsPresContext* presContext = GetPresContext();
-    if (presContext) {
-      nsLayoutUtils::DeregisterImageRequest(presContext,
-                                            aRequest,
-                                            nsnull);
-    }
-  }
-
-  if (requestSet && !requestSet->Length()) {
-    mFrameToRequestMap.Remove(aFrame);
-  }
-}
-
-void
-ImageLoader::DropRequestsForFrame(nsIFrame* aFrame)
-{
-  RequestSet* requestSet = nsnull;
-  if (!mFrameToRequestMap.Get(aFrame, &requestSet)) {
-    return;
-  }
-
-  NS_ASSERTION(requestSet, "This should never be null");
-
-  RequestSet frozenRequestSet(*requestSet);
-  for (RequestSet::size_type i = frozenRequestSet.Length(); i != 0; --i) {
-    imgIRequest* request = frozenRequestSet.ElementAt(i - 1);
-
-    DisassociateRequestFromFrame(request, aFrame);
-  }
-}
-
-void
-ImageLoader::SetAnimationMode(PRUint16 aMode)
-{
-  NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
-               aMode == imgIContainer::kDontAnimMode ||
-               aMode == imgIContainer::kLoopOnceAnimMode,
-               "Wrong Animation Mode is being set!");
-
-  mRequestToFrameMap.EnumerateRead(SetAnimationModeEnumerator, &aMode);
-}
-
-static PLDHashOperator
-ClearImageHashSet(nsPtrHashKey<nsCSSValue::Image>* aKey, void* aClosure)
-{
-  nsIDocument* doc = static_cast<nsIDocument*>(aClosure);
-  nsCSSValue::Image* image = aKey->GetKey();
-
-  imgIRequest* request = image->mRequests.GetWeak(doc);
-  if (request) {
-    request->CancelAndForgetObserver(NS_BINDING_ABORTED);
-  }
-
-  image->mRequests.Remove(doc);
-
-  return PL_DHASH_REMOVE;
-}
-
-void
-ImageLoader::ClearAll()
-{
-  mRequestToFrameMap.Clear();
-  mFrameToRequestMap.Clear();
-  mImages.EnumerateEntries(&ClearImageHashSet, mDocument);
-}
-
-void
-ImageLoader::LoadImage(nsIURI* aURI, nsIPrincipal* aOriginPrincipal,
-                       nsIURI* aReferrer, nsCSSValue::Image* aImage)
-{
-  NS_ASSERTION(aImage->mRequests.Count() == 0, "Huh?");
-
-  aImage->mRequests.Put(nsnull, nsnull);
-
-  if (!aURI) {
-    return;
-  }
-
-  if (!nsContentUtils::CanLoadImage(aURI, mDocument, mDocument,
-                                    aOriginPrincipal)) {
-    return;
-  }
-
-  nsCOMPtr<imgIRequest> request;
-  nsContentUtils::LoadImage(aURI, mDocument, aOriginPrincipal, aReferrer,
-                            nsnull, nsIRequest::LOAD_NORMAL,
-                            getter_AddRefs(request));
-
-  if (!request) {
-    return;
-  }
-
-  nsCOMPtr<imgIRequest> clonedRequest;
-  mInClone = true;
-  nsresult rv = request->Clone(this, getter_AddRefs(clonedRequest));
-  mInClone = false;
-
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  aImage->mRequests.Put(nsnull, request);
-  aImage->mRequests.Put(mDocument, clonedRequest);
-
-  AddImage(aImage);
-}
-
-void
-ImageLoader::AddImage(nsCSSValue::Image* aImage)
-{
-  NS_ASSERTION(!mImages.Contains(aImage), "Huh?");
-  if (!mImages.PutEntry(aImage)) {
-    NS_RUNTIMEABORT("OOM");
-  }
-}
-
-void
-ImageLoader::RemoveImage(nsCSSValue::Image* aImage)
-{
-  NS_ASSERTION(mImages.Contains(aImage), "Huh?");
-  mImages.RemoveEntry(aImage);
-}
-
-nsPresContext*
-ImageLoader::GetPresContext()
-{
-  if (!mDocument) {
-    return nsnull;
-  }
-
-  nsIPresShell* shell = mDocument->GetShell();
-  if (!shell) {
-    return nsnull;
-  }
-
-  return shell->GetPresContext();
-}
-
-void
-ImageLoader::DoRedraw(FrameSet* aFrameSet)
-{
-  NS_ASSERTION(aFrameSet, "Must have a frame set");
-  NS_ASSERTION(mDocument, "Should have returned earlier!");
-  NS_ASSERTION(mHavePainted, "Should have returned earlier!");
-
-  FrameSet::size_type length = aFrameSet->Length();
-  for (FrameSet::size_type i = 0; i < length; i++) {
-    nsIFrame* frame = aFrameSet->ElementAt(i);
-
-    // NOTE: It is not sufficient to invalidate only the size of the image:
-    //       the image may be tiled! 
-    //       The best option is to call into the frame, however lacking this
-    //       we have to at least invalidate the frame's bounds, hence
-    //       as long as we have a frame we'll use its size.
-    //
-
-    // Invalidate the entire frame
-    // XXX We really only need to invalidate the client area of the frame...    
-
-    nsRect bounds(nsPoint(0, 0), frame->GetSize());
-
-    if (frame->GetType() == nsGkAtoms::canvasFrame) {
-      // The canvas's background covers the whole viewport.
-      bounds = frame->GetVisualOverflowRect();
-    }
-
-    if (frame->GetStyleVisibility()->IsVisible()) {
-      frame->Invalidate(bounds);
-    }
-  }
-}
-
-NS_IMPL_ADDREF(ImageLoader)
-NS_IMPL_RELEASE(ImageLoader)
-
-NS_INTERFACE_MAP_BEGIN(ImageLoader)
-  NS_INTERFACE_MAP_ENTRY(imgIDecoderObserver)
-  NS_INTERFACE_MAP_ENTRY(imgIContainerObserver)
-  NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker)
-NS_INTERFACE_MAP_END
-
-NS_IMETHODIMP
-ImageLoader::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
-{ 
-  nsPresContext* presContext = GetPresContext();
-  if (!presContext) {
-    return NS_OK;
-  }
-
-  aImage->SetAnimationMode(presContext->ImageAnimationMode());
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ImageLoader::OnImageIsAnimated(imgIRequest* aRequest)
-{
-  // NB: Don't ignore this when cloning, it's our only chance to register
-  // the request with the refresh driver.
-  if (!mDocument) {
-    return NS_OK;
-  }
-
-  // Register with the refresh driver now that we are aware that
-  // we are animated.
-  nsPresContext* presContext = GetPresContext();
-  if (presContext) {
-    nsLayoutUtils::RegisterImageRequest(presContext,
-                                        aRequest,
-                                        nsnull);
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ImageLoader::OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame)
-{
-  if (!mDocument || !mHavePainted || mInClone) {
-    return NS_OK;
-  }
-
-  FrameSet* frameSet = nsnull;
-  if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
-    return NS_OK;
-  }
-
-  NS_ASSERTION(frameSet, "This should never be null!");
-
-  DoRedraw(frameSet);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ImageLoader::FrameChanged(imgIRequest *aRequest,
-                          imgIContainer *aContainer,
-                          const nsIntRect *aDirtyRect)
-{
-  if (!mDocument || !mHavePainted || mInClone) {
-    return NS_OK;
-  }
-
-  FrameSet* frameSet = nsnull;
-  if (!mRequestToFrameMap.Get(aRequest, &frameSet)) {
-    return NS_OK;
-  }
-
-  NS_ASSERTION(frameSet, "This should never be null!");
-
-  DoRedraw(frameSet);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ImageLoader::BlockOnload(imgIRequest* aRequest)
-{
-  if (!mDocument) {
-    return NS_OK;
-  }
-
-  mDocument->BlockOnload();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ImageLoader::UnblockOnload(imgIRequest* aRequest)
-{
-  if (!mDocument) {
-    return NS_OK;
-  }
-
-  mDocument->UnblockOnload(false);
-
-  return NS_OK;
-}
-
-} // namespace css
-} // namespace mozilla
deleted file mode 100644
--- a/layout/style/ImageLoader.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// A class that handles style system image loads (other image loads are handled
-// by the nodes in the content tree).
-
-#include "nsAutoPtr.h"
-#include "nsClassHashtable.h"
-#include "nsHashKeys.h"
-#include "nsInterfaceHashtable.h"
-#include "nsCSSValue.h"
-#include "imgIRequest.h"
-#include "imgIOnloadBlocker.h"
-#include "nsStubImageDecoderObserver.h"
-
-class nsIFrame;
-class nsIDocument;
-class nsPresContext;
-class nsIURI;
-class nsIPrincipal;
-
-namespace mozilla {
-namespace css {
-
-class ImageLoader : public nsStubImageDecoderObserver,
-                    public imgIOnloadBlocker {
-public:
-  ImageLoader(nsIDocument* aDocument)
-  : mDocument(aDocument),
-    mHavePainted(false),
-    mInClone(false)
-  { }
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_IMGIONLOADBLOCKER
-
-  // imgIDecoderObserver (override nsStubImageDecoderObserver)
-  NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
-  NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
-  NS_IMETHOD OnImageIsAnimated(imgIRequest *aRequest);
-  // Do not override OnDataAvailable since background images are not
-  // displayed incrementally; they are displayed after the entire image
-  // has been loaded.
-
-  // imgIContainerObserver (override nsStubImageDecoderObserver)
-  NS_IMETHOD FrameChanged(imgIRequest* aRequest,
-                          imgIContainer *aContainer,
-                          const nsIntRect *aDirtyRect);
-
-  nsresult Init();
-
-  inline void NotifyPaint()
-  {
-    mHavePainted = true;
-  }
-
-  void DropDocumentReference();
-
-  void MaybeRegisterCSSImage(nsCSSValue::Image* aImage);
-  void DeregisterCSSImage(nsCSSValue::Image* aImage);
-
-  void AssociateRequestToFrame(imgIRequest* aRequest,
-                               nsIFrame* aFrame);
-
-  void DisassociateRequestFromFrame(imgIRequest* aRequest,
-                                    nsIFrame* aFrame);
-
-  void DropRequestsForFrame(nsIFrame* aFrame);
-
-  void SetAnimationMode(PRUint16 aMode);
-
-  void ClearAll();
-
-  void LoadImage(nsIURI* aURI, nsIPrincipal* aPrincipal, nsIURI* aReferrer,
-                 nsCSSValue::Image* aCSSValue);
-
-  void DestroyRequest(imgIRequest* aRequest);
-
-private:
-  // We need to be able to look up the frames associated with a request (for
-  // delivering notifications) and the requests associated with a frame (when
-  // the frame goes away). Thus we maintain hashtables going both ways.  These
-  // should always be in sync.
-
-  typedef nsTArray<nsIFrame*> FrameSet;
-  typedef nsTArray<nsCOMPtr<imgIRequest> > RequestSet;
-  typedef nsTHashtable<nsPtrHashKey<nsCSSValue::Image> > ImageHashSet;
-  typedef nsClassHashtable<nsISupportsHashKey,
-                           FrameSet> RequestToFrameMap;
-  typedef nsClassHashtable<nsPtrHashKey<nsIFrame>,
-                           RequestSet> FrameToRequestMap;
-
-  void AddImage(nsCSSValue::Image* aCSSImage);
-  void RemoveImage(nsCSSValue::Image* aCSSImage);
-
-  nsPresContext* GetPresContext();
-
-  void DoRedraw(FrameSet* aFrameSet);
-
-  static PLDHashOperator
-  SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue,
-                             void* aClosure);
-
-  // A map of imgIRequests to the nsIFrames that are using them.
-  RequestToFrameMap mRequestToFrameMap;
-
-  // A map of nsIFrames to the imgIRequests they use.
-  FrameToRequestMap mFrameToRequestMap;
-
-  // A weak pointer to our document. Nulled out by DropDocumentReference.
-  nsIDocument* mDocument;
-
-  // The set of all nsCSSValue::Images (whether they're associated a frame or
-  // not).  We'll need this when we go away to remove any requests associated
-  // with our document from those Images.
-  ImageHashSet mImages;
-
-  // Have we painted yet? If not, no need to deliver notifications.
-  bool mHavePainted;
-
-  // Are we cloning?  If so, ignore any notifications we get.
-  bool mInClone;
-};
-
-} // namespace css
-} // namespace mozilla
--- a/layout/style/Makefile.in
+++ b/layout/style/Makefile.in
@@ -96,31 +96,29 @@ EXPORTS		= \
 		nsStyleStructList.h \
 		nsStyleTransformMatrix.h \
 		nsStyleUtil.h \
 		$(NULL)
 
 EXPORTS_mozilla/css = \
 		Declaration.h \
 		GroupRule.h \
-		ImageLoader.h \
 		ImportRule.h \
 		Loader.h \
 		NameSpaceRule.h \
 		Rule.h \
 		StyleRule.h \
 		$(NULL)
 
 CPPSRCS		= \
 		AnimationCommon.cpp \
 		nsCSSAnonBoxes.cpp \
 		nsCSSDataBlock.cpp \
 		Declaration.cpp \
 		nsCSSKeywords.cpp \
-		ImageLoader.cpp \
 		Loader.cpp \
 		nsAnimationManager.cpp \
 		nsCSSParser.cpp \
 		nsCSSProps.cpp \
 		nsCSSPseudoClasses.cpp \
 		nsCSSPseudoElements.cpp \
 		nsCSSRuleProcessor.cpp \
 		nsCSSRules.cpp \
--- a/layout/style/nsCSSDataBlock.cpp
+++ b/layout/style/nsCSSDataBlock.cpp
@@ -37,17 +37,16 @@
 
 /*
  * compact representation of the property-value pairs within a CSS
  * declaration, and the code for expanding and compacting it
  */
 
 #include "nsCSSDataBlock.h"
 #include "mozilla/css/Declaration.h"
-#include "mozilla/css/ImageLoader.h"
 #include "nsRuleData.h"
 #include "nsStyleSet.h"
 #include "nsStyleContext.h"
 
 namespace css = mozilla::css;
 
 /**
  * Does a fast move of aSource to aDest.  The previous value in
@@ -75,35 +74,26 @@ ShouldIgnoreColors(nsRuleData *aRuleData
 
 /**
  * Tries to call |nsCSSValue::StartImageLoad()| on an image source.
  * Image sources are specified by |url()| or |-moz-image-rect()| function.
  */
 static void
 TryToStartImageLoadOnValue(const nsCSSValue& aValue, nsIDocument* aDocument)
 {
-  MOZ_ASSERT(aDocument);
-
   if (aValue.GetUnit() == eCSSUnit_URL) {
     aValue.StartImageLoad(aDocument);
   }
-  else if (aValue.GetUnit() == eCSSUnit_Image) {
-    // If we already have a request, see if this document needs to clone it.
-    imgIRequest* request = aValue.GetImageValue(nsnull);
-
-    if (request) {
-      aDocument->StyleImageLoader()->MaybeRegisterCSSImage(aValue.GetImageStructValue());
-    }
-  }
   else if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) {
     nsCSSValue::Array* arguments = aValue.GetArrayValue();
     NS_ABORT_IF_FALSE(arguments->Count() == 6, "unexpected num of arguments");
 
     const nsCSSValue& image = arguments->Item(1);
-    TryToStartImageLoadOnValue(image, aDocument);
+    if (image.GetUnit() == eCSSUnit_URL)
+      image.StartImageLoad(aDocument);
   }
 }
 
 static void
 TryToStartImageLoad(const nsCSSValue& aValue, nsIDocument* aDocument,
                     nsCSSProperty aProperty)
 {
   if (aValue.GetUnit() == eCSSUnit_List) {
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -42,17 +42,16 @@
 
 #include "imgIRequest.h"
 #include "nsIPrincipal.h"
 #include "nsCSSProps.h"
 #include "nsContentUtils.h"
 #include "nsStyleUtil.h"
 #include "CSSCalc.h"
 #include "nsNetUtil.h"
-#include "mozilla/css/ImageLoader.h"
 
 namespace css = mozilla::css;
 
 nsCSSValue::nsCSSValue(PRInt32 aValue, nsCSSUnit aUnit)
   : mUnit(aUnit)
 {
   NS_ABORT_IF_FALSE(aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated ||
                     aUnit == eCSSUnit_EnumColor, "not an int value");
@@ -268,20 +267,20 @@ double nsCSSValue::GetAngleValueInRadian
   case eCSSUnit_Grad:   return angle * M_PI / 200.0;
 
   default:
     NS_ABORT_IF_FALSE(false, "unrecognized angular unit");
     return 0.0;
   }
 }
 
-imgIRequest* nsCSSValue::GetImageValue(nsIDocument* aDocument) const
+imgIRequest* nsCSSValue::GetImageValue() const
 {
   NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Image, "not an Image value");
-  return mValue.mImage->mRequests.GetWeak(aDocument);
+  return mValue.mImage->mRequest;
 }
 
 nscoord nsCSSValue::GetFixedLength(nsPresContext* aPresContext) const
 {
   NS_ABORT_IF_FALSE(mUnit == eCSSUnit_PhysicalMillimeter,
                     "not a fixed length unit");
 
   float inches = mValue.mFloat / MM_PER_INCH_FLOAT;
@@ -1675,53 +1674,27 @@ nsCSSValue::URL::SizeOfIncludingThis(nsM
 nsCSSValue::Image::Image(nsIURI* aURI, nsStringBuffer* aString,
                          nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal,
                          nsIDocument* aDocument)
   : URL(aURI, aString, aReferrer, aOriginPrincipal)
 {
   if (aDocument->GetOriginalDocument()) {
     aDocument = aDocument->GetOriginalDocument();
   }
-
-  if (!mRequests.Init()) {
-    NS_RUNTIMEABORT("out of memory");
+  if (aURI &&
+      nsContentUtils::CanLoadImage(aURI, aDocument, aDocument,
+                                   aOriginPrincipal)) {
+    nsContentUtils::LoadImage(aURI, aDocument, aOriginPrincipal, aReferrer,
+                              nsnull, nsIRequest::LOAD_NORMAL,
+                              getter_AddRefs(mRequest));
   }
-
-  aDocument->StyleImageLoader()->LoadImage(aURI, aOriginPrincipal, aReferrer,
-                                           this);
-}
-
-static PLDHashOperator
-ClearRequestHashtable(nsISupports* aKey, nsCOMPtr<imgIRequest>& aValue,
-                      void* aClosure)
-{
-  nsCSSValue::Image* image = static_cast<nsCSSValue::Image*>(aClosure);
-  nsIDocument* doc = static_cast<nsIDocument*>(aKey);
-
-#ifdef DEBUG
-  {
-    nsCOMPtr<nsIDocument> slowDoc = do_QueryInterface(aKey);
-    MOZ_ASSERT(slowDoc == doc);
-  }
-#endif
-
-  if (doc) {
-    doc->StyleImageLoader()->DeregisterCSSImage(image);
-  }
-
-  if (aValue) {
-    aValue->CancelAndForgetObserver(NS_BINDING_ABORTED);
-  }
-
-  return PL_DHASH_REMOVE;
 }
 
 nsCSSValue::Image::~Image()
 {
-  mRequests.Enumerate(&ClearRequestHashtable, this);
 }
 
 nsCSSValueGradientStop::nsCSSValueGradientStop()
   : mLocation(eCSSUnit_None),
     mColor(eCSSUnit_Null)
 {
   MOZ_COUNT_CTOR(nsCSSValueGradientStop);
 }
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -43,29 +43,26 @@
 #include "mozilla/Attributes.h"
 
 #include "nsCOMPtr.h"
 #include "nsCRTGlue.h"
 #include "nsCSSKeywords.h"
 #include "nsCSSProperty.h"
 #include "nsColor.h"
 #include "nsCoord.h"
-#include "nsInterfaceHashtable.h"
 #include "nsString.h"
 #include "nsStringBuffer.h"
 #include "nsTArray.h"
 #include "nsStyleConsts.h"
 
 class imgIRequest;
 class nsIDocument;
 class nsIPrincipal;
 class nsPresContext;
 class nsIURI;
-template <class T>
-class nsPtrHashKey;
 
 // Deletes a linked list iteratively to avoid blowing up the stack (bug 456196).
 #define NS_CSS_DELETE_LIST_MEMBER(type_, ptr_, member_)                        \
   {                                                                            \
     type_ *cur = (ptr_)->member_;                                              \
     (ptr_)->member_ = nsnull;                                                  \
     while (cur) {                                                              \
       type_ *next = cur->member_;                                              \
@@ -384,35 +381,29 @@ public:
   URL* GetURLStructValue() const
   {
     // Not allowing this for Image values, because if the caller takes
     // a ref to them they won't be able to delete them properly.
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_URL, "not a URL value");
     return mValue.mURL;
   }
 
-  Image* GetImageStructValue() const
-  {
-    NS_ABORT_IF_FALSE(mUnit == eCSSUnit_Image, "not an Image value");
-    return mValue.mImage;
-  }
-
   const PRUnichar* GetOriginalURLValue() const
   {
     NS_ABORT_IF_FALSE(mUnit == eCSSUnit_URL || mUnit == eCSSUnit_Image,
                       "not a URL value");
     return GetBufferValue(mUnit == eCSSUnit_URL ?
                             mValue.mURL->mString :
                             mValue.mImage->mString);
   }
 
   // Not making this inline because that would force us to include
   // imgIRequest.h, which leads to REQUIRES hell, since this header is included
   // all over.
-  imgIRequest* GetImageValue(nsIDocument* aDocument) const;
+  imgIRequest* GetImageValue() const;
 
   nscoord GetFixedLength(nsPresContext* aPresContext) const;
   nscoord GetPixelLength() const;
 
   void Reset()  // sets to null
   {
     if (mUnit != eCSSUnit_Null)
       DoReset();
@@ -521,17 +512,17 @@ public:
     // this header is included all over.
     // aString must not be null.
     Image(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer,
           nsIPrincipal* aOriginPrincipal, nsIDocument* aDocument);
     ~Image();
 
     // Inherit operator== from nsCSSValue::URL
 
-    nsInterfaceHashtable<nsISupportsHashKey, imgIRequest> mRequests; 
+    nsCOMPtr<imgIRequest> mRequest; // null == image load blocked or somehow failed
 
     // Override AddRef and Release to not only log ourselves correctly, but
     // also so that we delete correctly without a virtual destructor
     NS_INLINE_DECL_REFCOUNTING(nsCSSValue::Image)
   };
 
 private:
   static const PRUnichar* GetBufferValue(nsStringBuffer* aBuffer) {
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -98,22 +98,16 @@ using namespace mozilla::dom;
 #define NS_SET_IMAGE_REQUEST(method_, context_, request_)                   \
   if ((context_)->PresContext()->IsDynamic()) {                               \
     method_(request_);                                                      \
   } else {                                                                  \
     nsCOMPtr<imgIRequest> req = nsContentUtils::GetStaticRequest(request_); \
     method_(req);                                                           \
   }
 
-#define NS_SET_IMAGE_REQUEST_WITH_DOC(method_, context_, requestgetter_)      \
-  {                                                                           \
-    nsIDocument* doc = (context_)->PresContext()->Document();                 \
-    NS_SET_IMAGE_REQUEST(method_, context_, requestgetter_(doc))              \
-  }
-
 /*
  * For storage of an |nsRuleNode|'s children in a PLDHashTable.
  */
 
 struct ChildrenHashEntry : public PLDHashEntryHdr {
   // key is |mRuleNode->GetKey()|
   nsRuleNode *mRuleNode;
 };
@@ -934,19 +928,19 @@ static void SetStyleImageToImageRect(nsS
                     aValue.EqualsFunction(eCSSKeyword__moz_image_rect),
                     "the value is not valid -moz-image-rect()");
 
   nsCSSValue::Array* arr = aValue.GetArrayValue();
   NS_ABORT_IF_FALSE(arr && arr->Count() == 6, "invalid number of arguments");
 
   // <uri>
   if (arr->Item(1).GetUnit() == eCSSUnit_Image) {
-    NS_SET_IMAGE_REQUEST_WITH_DOC(aResult.SetImageData,
-                                  aStyleContext,
-                                  arr->Item(1).GetImageValue)
+    NS_SET_IMAGE_REQUEST(aResult.SetImageData,
+                         aStyleContext,
+                         arr->Item(1).GetImageValue())
   } else {
     NS_WARNING("nsCSSValue::Image::Image() failed?");
   }
 
   // <top>, <right>, <bottom>, <left>
   nsStyleSides cropRect;
   NS_FOR_CSS_SIDES(side) {
     nsStyleCoord coord;
@@ -966,19 +960,19 @@ static void SetStyleImage(nsStyleContext
                           const nsCSSValue& aValue,
                           nsStyleImage& aResult,
                           bool& aCanStoreInRuleTree)
 {
   aResult.SetNull();
 
   switch (aValue.GetUnit()) {
     case eCSSUnit_Image:
-      NS_SET_IMAGE_REQUEST_WITH_DOC(aResult.SetImageData,
-                                    aStyleContext,
-                                    aValue.GetImageValue)
+      NS_SET_IMAGE_REQUEST(aResult.SetImageData,
+                           aStyleContext,
+                           aValue.GetImageValue())
       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");
       }
       break;
@@ -3626,32 +3620,31 @@ nsRuleNode::ComputeUserInterfaceData(voi
       // The parser will never create a list that is *all* URL values --
       // that's invalid.
       NS_ABORT_IF_FALSE(cursorUnit == eCSSUnit_List ||
                         cursorUnit == eCSSUnit_ListDep,
                         nsPrintfCString(64, "unrecognized cursor unit %d",
                                         cursorUnit).get());
       const nsCSSValueList* list = cursorValue->GetListValue();
       const nsCSSValueList* list2 = list;
-      nsIDocument* doc = aContext->PresContext()->Document();
       PRUint32 arrayLength = 0;
       for ( ; list->mValue.GetUnit() == eCSSUnit_Array; list = list->mNext)
-        if (list->mValue.GetArrayValue()->Item(0).GetImageValue(doc))
+        if (list->mValue.GetArrayValue()->Item(0).GetImageValue())
           ++arrayLength;
 
       if (arrayLength != 0) {
         ui->mCursorArray = new nsCursorImage[arrayLength];
         if (ui->mCursorArray) {
           ui->mCursorArrayLength = arrayLength;
 
           for (nsCursorImage *item = ui->mCursorArray;
                list2->mValue.GetUnit() == eCSSUnit_Array;
                list2 = list2->mNext) {
             nsCSSValue::Array *arr = list2->mValue.GetArrayValue();
-            imgIRequest *req = arr->Item(0).GetImageValue(doc);
+            imgIRequest *req = arr->Item(0).GetImageValue();
             if (req) {
               item->SetImage(req);
               if (arr->Item(1).GetUnit() != eCSSUnit_Null) {
                 item->mHaveHotspot = true;
                 item->mHotspotX = arr->Item(1).GetFloatValue(),
                 item->mHotspotY = arr->Item(2).GetFloatValue();
               }
               ++item;
@@ -5729,23 +5722,21 @@ nsRuleNode::ComputeBorderData(void* aSta
   SetDiscrete(*aRuleData->ValueForFloatEdge(),
               border->mFloatEdge, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentBorder->mFloatEdge,
               NS_STYLE_FLOAT_EDGE_CONTENT, 0, 0, 0, 0);
 
   // border-image-source
   const nsCSSValue* borderImageSource = aRuleData->ValueForBorderImageSource();
   if (borderImageSource->GetUnit() == eCSSUnit_Image) {
-    NS_SET_IMAGE_REQUEST_WITH_DOC(border->SetBorderImage,
-                                  aContext,
-                                  borderImageSource->GetImageValue);
+    NS_SET_IMAGE_REQUEST(border->SetBorderImage, aContext,
+                         borderImageSource->GetImageValue());
   } else if (borderImageSource->GetUnit() == eCSSUnit_Inherit) {
     canStoreInRuleTree = false;
-    NS_SET_IMAGE_REQUEST(border->SetBorderImage,
-                         aContext,
+    NS_SET_IMAGE_REQUEST(border->SetBorderImage, aContext,
                          parentBorder->GetBorderImage());
   } else if (borderImageSource->GetUnit() == eCSSUnit_Initial ||
              borderImageSource->GetUnit() == eCSSUnit_None) {
     border->SetBorderImage(nsnull);
   }
 
   nsCSSValue borderImageSliceValue;
   nsCSSValue borderImageSliceFill;
@@ -5986,19 +5977,19 @@ nsRuleNode::ComputeListData(void* aStart
   SetDiscrete(*aRuleData->ValueForListStyleType(),
               list->mListStyleType, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentList->mListStyleType,
               NS_STYLE_LIST_STYLE_DISC, 0, 0, 0, 0);
 
   // list-style-image: url, none, inherit
   const nsCSSValue* imageValue = aRuleData->ValueForListStyleImage();
   if (eCSSUnit_Image == imageValue->GetUnit()) {
-    NS_SET_IMAGE_REQUEST_WITH_DOC(list->SetListStyleImage,
-                                  aContext,
-                                  imageValue->GetImageValue)
+    NS_SET_IMAGE_REQUEST(list->SetListStyleImage,
+                         aContext,
+                         imageValue->GetImageValue())
   }
   else if (eCSSUnit_None == imageValue->GetUnit() ||
            eCSSUnit_Initial == imageValue->GetUnit()) {
     list->SetListStyleImage(nsnull);
   }
   else if (eCSSUnit_Inherit == imageValue->GetUnit()) {
     canStoreInRuleTree = false;
     NS_SET_IMAGE_REQUEST(list->SetListStyleImage,
@@ -6307,19 +6298,17 @@ nsRuleNode::ComputeContentData(void* aSt
               NS_ERROR("bad content value");
             }
             break;
           default:
             NS_ERROR("bad content type");
           }
           data.mType = type;
           if (type == eStyleContentType_Image) {
-            NS_SET_IMAGE_REQUEST_WITH_DOC(data.SetImage,
-                                          aContext,
-                                          value.GetImageValue);
+            NS_SET_IMAGE_REQUEST(data.SetImage, aContext, value.GetImageValue());
           }
           else if (type <= eStyleContentType_Attr) {
             value.GetStringValue(buffer);
             data.mContent.mString = NS_strdup(buffer.get());
           }
           else if (type <= eStyleContentType_Counters) {
             data.mContent.mCounters = value.GetArrayValue();
             data.mContent.mCounters->AddRef();