Bug 430061: Don't use necko's memory cache in imglib; r/sr=stuart,vlad,bz
authorJoe Drew <joe@drew.ca>
Thu, 04 Sep 2008 19:00:42 -0400
changeset 18827 44853b2a5fc2
parent 18826 0acaf202f890
child 18828 0094ca20c192
push id1745
push userjdrew@mozilla.com
push dateFri, 05 Sep 2008 04:32:44 +0000
treeherdermozilla-central@44853b2a5fc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs430061
milestone1.9.1b1pre
Bug 430061: Don't use necko's memory cache in imglib; r/sr=stuart,vlad,bz ? .fast-update ? _profile ? _tests ? obj-ff-debug ? staticlib ? README/.fast-update ? browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf ? build/pgo/automation.py ? build/pgo/profileserver.py ? config/buildid ? config/system_wrappers ? content/base/test/TestNativeXMLHttpRequest ? content/base/test/TestPlainTextSerializer ? embedding/components/printingui/src/mac/printpde/build ? gfx/thebes/public/.gfxContext.h.swp ? gfx/thebes/test/gfxFontSelectionTest ? gfx/thebes/test/gfxSurfaceRefCountTest ? gfx/thebes/test/gfxTextRunPerfTest ? gfx/thebes/test/gfxWordCacheTest ? intl/uconv/tests/TestUConv ? intl/uconv/tests/nsconv ? intl/uconv/tests/plattest ? intl/unicharutil/tests/NormalizationTest ? js/src/host_jskwgen ? js/src/jsautokw.h ? layout/style/test/css_properties.js ? layout/style/test/host_ListCSSProperties ? layout/tools/reftest/autoconf.js ? modules/libpr0n/src/.imgContainer.cpp.swp ? modules/libpr0n/src/.imgLoader.cpp.swp ? modules/libpr0n/src/.imgLoader.h.swp ? modules/libpr0n/src/.imgRequestProxy.cpp.swp ? modules/libpr0n/src/check-all-at-removal-time ? modules/libpr0n/src/currpatch ? modules/libpr0n/src/update-every-time ? modules/plugin/samples/default/mac/build ? netwerk/cache/src/.nsMemoryCacheDevice.cpp.swp ? netwerk/dns/src/etld_data.inc ? netwerk/test/ReadNTLM ? netwerk/test/TestCookie ? netwerk/test/TestIncrementalDownload ? netwerk/test/TestOpen ? netwerk/test/TestServ ? netwerk/test/TestStreamLoader ? netwerk/test/TestUDPSocketProvider ? nsprpub/.fast-update ? nsprpub/unallmakefiles ? parser/htmlparser/robot/test/htmlrobot ? parser/htmlparser/tests/grabpage/grabpage ? parser/htmlparser/tests/html/TestParser ? rdf/tests/triplescat/triplescat ? storage/test/teststorage1 ? testing/mochitest/automation.py ? testing/mochitest/automation.pyc ? testing/mochitest/runtests.pl ? testing/mochitest/runtests.py ? testing/mochitest/ssltunnel/ssltunnel ? toolkit/components/url-classifier/tests/TestUrlClassifierUtils ? toolkit/crashreporter/client/crashreporter ? toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/dump_syms ? toolkit/crashreporter/test/TestCrashReporterAPI ? toolkit/library/XUL ? toolkit/mozapps/update/src/nsUpdateService.js ? toolkit/xre/platform.ini ? tools/rb ? tools/trace-malloc ? widget/src/cocoa/libwidget.rsrc ? xpcom/io/.nsStringStream.cpp.swp ? xpcom/proxy/tests/proxy-create-threadsafety ? xpcom/sample/program/nsTestSample ? xpcom/tests/TestAutoPtr ? xpcom/tests/TestExpirationTracker ? xpcom/tests/TestHashtables ? xpcom/tests/TestINIParser ? xpcom/tests/TestPipe ? xpcom/tests/TestProxies ? xpcom/tests/TestRegistrationOrder ? xpcom/tests/TestStorageStream ? xpcom/tests/TestStringAPI ? xpcom/tests/TestStrings ? xpcom/tests/TestTArray ? xpcom/tests/TestTextFormatter ? xpcom/tests/TestThreadPool ? xpcom/tests/TestVersionComparator ? xpcom/tests/external/TestMinStringAPI ? xpfe/bootstrap/appleevents/mozillaSuite.rsrc Index: modules/libpr0n/build/nsImageModule.cpp =================================================================== RCS file: /cvsroot/mozilla/modules/libpr0n/build/nsImageModule.cpp,v retrieving revision 1.20
modules/libpr0n/build/nsImageModule.cpp
modules/libpr0n/src/Makefile.in
modules/libpr0n/src/imgCache.cpp
modules/libpr0n/src/imgCache.h
modules/libpr0n/src/imgLoader.cpp
modules/libpr0n/src/imgLoader.h
modules/libpr0n/src/imgRequest.cpp
modules/libpr0n/src/imgRequest.h
modules/libpr0n/test/Makefile.in
modules/libpref/src/init/all.js
--- a/modules/libpr0n/build/nsImageModule.cpp
+++ b/modules/libpr0n/build/nsImageModule.cpp
@@ -48,17 +48,16 @@
 #endif
 
 #include "nsIGenericFactory.h"
 #include "nsIModule.h"
 #include "nsICategoryManager.h"
 #include "nsXPCOMCID.h"
 #include "nsServiceManagerUtils.h"
 
-#include "imgCache.h"
 #include "imgContainer.h"
 #include "imgLoader.h"
 #include "imgRequest.h"
 #include "imgRequestProxy.h"
 #include "imgTools.h"
 
 #ifdef IMG_BUILD_DECODER_gif
 // gif
@@ -94,17 +93,16 @@
 #ifdef IMG_BUILD_ENCODER_jpeg
 // jpeg
 #include "nsJPEGEncoder.h"
 #endif
 
 
 // objects that just require generic constructors
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(imgCache)
 NS_GENERIC_FACTORY_CONSTRUCTOR(imgContainer)
 NS_GENERIC_FACTORY_CONSTRUCTOR(imgLoader)
 NS_GENERIC_FACTORY_CONSTRUCTOR(imgRequestProxy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(imgTools)
 
 #ifdef IMG_BUILD_DECODER_gif
 // gif
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsGIFDecoder2)
@@ -198,19 +196,19 @@ static NS_METHOD ImageUnregisterProc(nsI
     catMan->DeleteCategoryEntry("Gecko-Content-Viewers", gImageMimeTypes[i], PR_TRUE);
 
   return NS_OK;
 }
 
 static const nsModuleComponentInfo components[] =
 {
   { "image cache",
-    NS_IMGCACHE_CID,
+    NS_IMGLOADER_CID,
     "@mozilla.org/image/cache;1",
-    imgCacheConstructor, },
+    imgLoaderConstructor, },
   { "image container",
     NS_IMGCONTAINER_CID,
     "@mozilla.org/image/container;1",
     imgContainerConstructor, },
   { "image loader",
     NS_IMGLOADER_CID,
     "@mozilla.org/image/loader;1",
     imgLoaderConstructor,
@@ -310,21 +308,21 @@ static const nsModuleComponentInfo compo
      "@mozilla.org/image/decoder;2?type=image/xbm",
      nsXBMDecoderConstructor, },
 #endif
 };
 
 PR_STATIC_CALLBACK(nsresult)
 imglib_Initialize(nsIModule* aSelf)
 {
-  imgCache::Init();
+  imgLoader::InitCache();
   return NS_OK;
 }
 
 PR_STATIC_CALLBACK(void)
 imglib_Shutdown(nsIModule* aSelf)
 {
-  imgCache::Shutdown();
+  imgLoader::Shutdown();
 }
 
 NS_IMPL_NSGETMODULE_WITH_CTOR_DTOR(nsImageLib2Module, components,
                                    imglib_Initialize, imglib_Shutdown)
 
--- a/modules/libpr0n/src/Makefile.in
+++ b/modules/libpr0n/src/Makefile.in
@@ -56,17 +56,16 @@ REQUIRES	= xpcom \
 		  gfx \
 		  thebes \
 		  caps \
 		  xpconnect \
 		  js \
 		  $(NULL)
 
 CPPSRCS		= \
-			imgCache.cpp     \
 			imgContainer.cpp \
 			imgLoader.cpp    \
 			imgRequest.cpp   \
 			imgRequestProxy.cpp \
 			imgTools.cpp
 
 include $(topsrcdir)/config/rules.mk
 
deleted file mode 100644
--- a/modules/libpr0n/src/imgCache.cpp
+++ /dev/null
@@ -1,357 +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
- * 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 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 "imgCache.h"
-
-#include "ImageLogging.h"
-
-#include "imgRequest.h"
-
-#include "nsXPIDLString.h"
-#include "nsCOMPtr.h"
-#include "nsIServiceManager.h"
-#include "nsIMemory.h"
-#include "nsIObserverService.h"
-
-#include "nsICache.h"
-#include "nsICacheService.h"
-#include "nsICacheSession.h"
-#include "nsICacheEntryDescriptor.h"
-
-#include "nsIFile.h"
-#include "nsIFileURL.h"
-
-NS_IMPL_ISUPPORTS3(imgCache, imgICache, nsIObserver, nsISupportsWeakReference)
-
-imgCache::imgCache()
-{
-  /* member initializers and constructor code */
-}
-
-imgCache::~imgCache()
-{
-  /* destructor code */
-}
-
-nsresult imgCache::Init()
-{
-  nsresult rv;
-  nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1", &rv);
-  if (NS_FAILED(rv))
-    return rv;
-  
-  imgCache* cache = new imgCache();
-  if(!cache) return NS_ERROR_OUT_OF_MEMORY;
-
-  os->AddObserver(cache, "memory-pressure", PR_FALSE);
-  os->AddObserver(cache, "chrome-flush-skin-caches", PR_FALSE);
-  os->AddObserver(cache, "chrome-flush-caches", PR_FALSE);
-
-  return NS_OK;
-}
-
-/* void clearCache (in boolean chrome); */
-NS_IMETHODIMP imgCache::ClearCache(PRBool chrome)
-{
-  if (chrome)
-    return imgCache::ClearChromeImageCache();
-  else
-    return imgCache::ClearImageCache();
-}
-
-
-/* void removeEntry(in nsIURI uri); */
-NS_IMETHODIMP imgCache::RemoveEntry(nsIURI *uri)
-{
-  if (imgCache::Remove(uri))
-    return NS_OK;
-
-  return NS_ERROR_NOT_AVAILABLE;
-}
-
-/* imgIRequest findEntry(in nsIURI uri); */
-NS_IMETHODIMP imgCache::FindEntryProperties(nsIURI *uri, nsIProperties **_retval)
-{
-  PRBool expired;
-  // This is an owning reference that must be released.
-  imgRequest *request = nsnull;
-  nsCOMPtr<nsICacheEntryDescriptor> entry;
-
-  // addrefs request
-  imgCache::Get(uri, &expired, &request, getter_AddRefs(entry));
-
-  *_retval = nsnull;
-
-  if (request) {
-    *_retval = request->Properties();
-    NS_ADDREF(*_retval);
-  }
-
-  NS_IF_RELEASE(request);
-
-  return NS_OK;
-}
-
-
-static nsCOMPtr<nsICacheSession> gSession = nsnull;
-static nsCOMPtr<nsICacheSession> gChromeSession = nsnull;
-
-void GetCacheSession(nsIURI *aURI, nsICacheSession **_retval)
-{
-  NS_ASSERTION(aURI, "Null URI!");
-
-  PRBool isChrome = PR_FALSE;
-  aURI->SchemeIs("chrome", &isChrome);
-
-  if (gSession && !isChrome) {
-    *_retval = gSession;
-    NS_ADDREF(*_retval);
-    return;
-  }
-
-  if (gChromeSession && isChrome) {
-    *_retval = gChromeSession;
-    NS_ADDREF(*_retval);
-    return;
-  }
-
-  nsCOMPtr<nsICacheService> cacheService(do_GetService("@mozilla.org/network/cache-service;1"));
-  if (!cacheService) {
-    NS_WARNING("Unable to get the cache service");
-    return;
-  }
-
-  nsCOMPtr<nsICacheSession> newSession;
-  cacheService->CreateSession(isChrome ? "image-chrome" : "image",
-                              nsICache::STORE_IN_MEMORY,
-                              nsICache::NOT_STREAM_BASED,
-                              getter_AddRefs(newSession));
-
-  if (!newSession) {
-    NS_WARNING("Unable to create a cache session");
-    return;
-  }
-
-  if (isChrome)
-    gChromeSession = newSession;
-  else {
-    gSession = newSession;
-    gSession->SetDoomEntriesIfExpired(PR_FALSE);
-  }
-
-  *_retval = newSession;
-  NS_ADDREF(*_retval);
-}
-
-
-void imgCache::Shutdown()
-{
-  gSession = nsnull;
-  gChromeSession = nsnull;
-}
-
-
-nsresult imgCache::ClearChromeImageCache()
-{
-  if (!gChromeSession)
-    return NS_OK;
-
-  return gChromeSession->EvictEntries();
-}
-
-nsresult imgCache::ClearImageCache()
-{
-  if (!gSession)
-    return NS_OK;
-
-  return gSession->EvictEntries();
-}
-
-
-
-PRBool imgCache::Put(nsIURI *aKey, imgRequest *request, nsICacheEntryDescriptor **aEntry)
-{
-  LOG_STATIC_FUNC(gImgLog, "imgCache::Put");
-
-  nsresult rv;
-
-  nsCOMPtr<nsICacheSession> ses;
-  GetCacheSession(aKey, getter_AddRefs(ses));
-  if (!ses) return PR_FALSE;
-
-  nsCAutoString spec;
-  aKey->GetAsciiSpec(spec);
-
-  nsCOMPtr<nsICacheEntryDescriptor> entry;
-
-  rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_WRITE, nsICache::BLOCKING, getter_AddRefs(entry));
-
-  if (NS_FAILED(rv) || !entry)
-    return PR_FALSE;
-
-  nsCOMPtr<nsISupports> sup = reinterpret_cast<nsISupports*>(request);
-  entry->SetCacheElement(sup);
-
-  entry->MarkValid();
-
-  // If file, force revalidation on expiration
-  PRBool isFile;
-  aKey->SchemeIs("file", &isFile);
-  if (isFile)
-    entry->SetMetaDataElement("MustValidateIfExpired", "true");
-
-  *aEntry = entry;
-  NS_ADDREF(*aEntry);
-
-  return PR_TRUE;
-}
-
-static PRUint32
-SecondsFromPRTime(PRTime prTime)
-{
-  PRInt64 microSecondsPerSecond, intermediateResult;
-  PRUint32 seconds;
-  
-  LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
-  LL_DIV(intermediateResult, prTime, microSecondsPerSecond);
-  LL_L2UI(seconds, intermediateResult);
-  return seconds;
-}
-
-
-PRBool imgCache::Get(nsIURI *aKey, PRBool *aHasExpired, imgRequest **aRequest, nsICacheEntryDescriptor **aEntry)
-{
-  LOG_STATIC_FUNC(gImgLog, "imgCache::Get");
-
-  nsresult rv;
-
-  nsCOMPtr<nsICacheSession> ses;
-  GetCacheSession(aKey, getter_AddRefs(ses));
-  if (!ses) return PR_FALSE;
-
-  nsCAutoString spec;
-  aKey->GetAsciiSpec(spec);
-
-  nsCOMPtr<nsICacheEntryDescriptor> entry;
-
-  rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, nsICache::BLOCKING, getter_AddRefs(entry));
-
-  if (NS_FAILED(rv) || !entry)
-    return PR_FALSE;
-
-  if (aHasExpired) {
-    PRUint32 expirationTime;
-    rv = entry->GetExpirationTime(&expirationTime);
-    if (NS_FAILED(rv) || (expirationTime <= SecondsFromPRTime(PR_Now()))) {
-      *aHasExpired = PR_TRUE;
-    } else {
-      *aHasExpired = PR_FALSE;
-    }
-    // Special treatment for file URLs - entry has expired if file has changed
-    nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aKey));
-    if (fileUrl) {
-      PRUint32 lastModTime;
-      entry->GetLastModified(&lastModTime);
-
-      nsCOMPtr<nsIFile> theFile;
-      rv = fileUrl->GetFile(getter_AddRefs(theFile));
-      if (NS_SUCCEEDED(rv)) {
-        PRInt64 fileLastMod;
-        rv = theFile->GetLastModifiedTime(&fileLastMod);
-        if (NS_SUCCEEDED(rv)) {
-          // nsIFile uses millisec, NSPR usec
-          PRInt64 one_thousand = LL_INIT(0, 1000);
-          LL_MUL(fileLastMod, fileLastMod, one_thousand);
-          *aHasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
-        }
-      }
-    }
-  }
-
-  nsCOMPtr<nsISupports> sup;
-  entry->GetCacheElement(getter_AddRefs(sup));
-
-  *aRequest = reinterpret_cast<imgRequest*>(sup.get());
-  NS_IF_ADDREF(*aRequest);
-
-  *aEntry = entry;
-  NS_ADDREF(*aEntry);
-
-  return PR_TRUE;
-}
-
-
-PRBool imgCache::Remove(nsIURI *aKey)
-{
-  LOG_STATIC_FUNC(gImgLog, "imgCache::Remove");
-  if (!aKey) return PR_FALSE;
-
-  nsresult rv;
-  nsCOMPtr<nsICacheSession> ses;
-  GetCacheSession(aKey, getter_AddRefs(ses));
-  if (!ses) return PR_FALSE;
-
-  nsCAutoString spec;
-  aKey->GetAsciiSpec(spec);
-
-  nsCOMPtr<nsICacheEntryDescriptor> entry;
-
-  rv = ses->OpenCacheEntry(spec, nsICache::ACCESS_READ, nsICache::BLOCKING, getter_AddRefs(entry));
-
-  if (NS_FAILED(rv) || !entry)
-    return PR_FALSE;
-
-  entry->Doom();
-
-  return PR_TRUE;
-}
-
-
-NS_IMETHODIMP
-imgCache::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
-{
-  if (strcmp(aTopic, "memory-pressure") == 0) {
-    ClearCache(PR_FALSE);
-    ClearCache(PR_TRUE);
-  } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
-             strcmp(aTopic, "chrome-flush-caches") == 0) {
-    ClearCache(PR_TRUE);
-  }
-  return NS_OK;
-}
deleted file mode 100644
--- a/modules/libpr0n/src/imgCache.h
+++ /dev/null
@@ -1,82 +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
- * 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 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 "imgICache.h"
-#include "nsIObserver.h"
-#include "nsWeakReference.h"
-
-#include "prtypes.h"
-
-class imgRequest;
-class nsIURI;
-class nsICacheEntryDescriptor;
-
-#define NS_IMGCACHE_CID \
-{ /* fb4fd28a-1dd1-11b2-8391-e14242c59a41 */         \
-     0xfb4fd28a,                                     \
-     0x1dd1,                                         \
-     0x11b2,                                         \
-    {0x83, 0x91, 0xe1, 0x42, 0x42, 0xc5, 0x9a, 0x41} \
-}
-
-class imgCache : public imgICache, 
-                 public nsIObserver,
-                 public nsSupportsWeakReference
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_IMGICACHE
-  NS_DECL_NSIOBSERVER
-
-  imgCache();
-  virtual ~imgCache();
-
-  static nsresult Init();
-
-  static void Shutdown(); // for use by the factory
-
-  /* additional members */
-  static PRBool Put(nsIURI *aKey, imgRequest *request, nsICacheEntryDescriptor **aEntry);
-  static PRBool Get(nsIURI *aKey, PRBool *aHasExpired, imgRequest **aRequest, nsICacheEntryDescriptor **aEntry);
-  static PRBool Remove(nsIURI *aKey);
-
-  static nsresult ClearChromeImageCache();
-  static nsresult ClearImageCache();
-};
-
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -39,25 +39,28 @@
 
 #include "imgLoader.h"
 
 #include "nsCOMPtr.h"
 
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "nsICachingChannel.h"
+#include "nsIObserverService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
 #include "nsIProxyObjectManager.h"
 #include "nsIServiceManager.h"
+#include "nsIFileURL.h"
 #include "nsThreadUtils.h"
 #include "nsXPIDLString.h"
 #include "nsCRT.h"
 
 #include "netCore.h"
 
-#include "imgCache.h"
 #include "imgRequest.h"
 #include "imgRequestProxy.h"
 
 #include "ImageErrors.h"
 #include "ImageLogging.h"
 
 #include "nsIComponentRegistrar.h"
 
@@ -98,47 +101,46 @@ static void PrintImageDecoders()
       if (StringBeginsWith(xcs, decoderContract)) {
         printf("Have decoder for mime type: %s\n", xcs.get()+decoderContract.Length());
       }
     }
   }
 }
 #endif
 
-NS_IMPL_ISUPPORTS2(imgLoader, imgILoader, nsIContentSniffer)
-
-imgLoader::imgLoader()
+static PRBool NewRequestAndEntry(nsIURI *uri, imgRequest **request, imgCacheEntry **entry)
 {
-  /* member initializers and constructor code */
-#ifdef DEBUG_pavlov
-  PrintImageDecoders();
-#endif
+  // If file, force revalidation on expiration
+  PRBool isFile;
+  uri->SchemeIs("file", &isFile);
+
+  *request = new imgRequest();
+  if (!*request)
+    return PR_FALSE;
+
+  *entry = new imgCacheEntry(*request, /* mustValidateIfExpired = */ isFile);
+  if (!*entry) {
+    delete *request;
+    return PR_FALSE;
+  }
+
+  NS_ADDREF(*request);
+  NS_ADDREF(*entry);
+
+  return PR_TRUE;
 }
 
-imgLoader::~imgLoader()
-{
-  /* destructor code */
-}
-
-#define LOAD_FLAGS_CACHE_MASK    (nsIRequest::LOAD_BYPASS_CACHE | \
-                                  nsIRequest::LOAD_FROM_CACHE)
-
-#define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS |   \
-                                  nsIRequest::VALIDATE_NEVER |    \
-                                  nsIRequest::VALIDATE_ONCE_PER_SESSION)
-
-
-static PRBool RevalidateEntry(nsICacheEntryDescriptor *aEntry,
+static PRBool ShouldRevalidateEntry(imgCacheEntry *aEntry,
                               nsLoadFlags aFlags,
                               PRBool aHasExpired)
 {
   PRBool bValidateEntry = PR_FALSE;
 
-  NS_ASSERTION(!(aFlags & nsIRequest::LOAD_BYPASS_CACHE),
-               "MUST not revalidate when BYPASS_CACHE is specified.");
+  if (aFlags & nsIRequest::LOAD_BYPASS_CACHE)
+    return PR_FALSE;
 
   if (aFlags & nsIRequest::VALIDATE_ALWAYS) {
     bValidateEntry = PR_TRUE;
   }
   //
   // The cache entry has expired...  Determine whether the stale cache
   // entry can be used without validation...
   //
@@ -146,37 +148,30 @@ static PRBool RevalidateEntry(nsICacheEn
     //
     // VALIDATE_NEVER and VALIDATE_ONCE_PER_SESSION allow stale cache
     // entries to be used unless they have been explicitly marked to
     // indicate that revalidation is necessary.
     //
     if (aFlags & (nsIRequest::VALIDATE_NEVER | 
                   nsIRequest::VALIDATE_ONCE_PER_SESSION)) 
     {
-      nsXPIDLCString value;
-
-      aEntry->GetMetaDataElement("MustValidateIfExpired",
-                                 getter_Copies(value));
-      if (PL_strcmp(value, "true")) {
-        bValidateEntry = PR_TRUE;
-      }
+      bValidateEntry = aEntry->GetMustValidateIfExpired();
     }
     //
     // LOAD_FROM_CACHE allows a stale cache entry to be used... Otherwise,
     // the entry must be revalidated.
     //
     else if (!(aFlags & nsIRequest::LOAD_FROM_CACHE)) {
       bValidateEntry = PR_TRUE;
     }
   }
 
   return bValidateEntry;
 }
 
-
 static nsresult NewImageChannel(nsIChannel **aResult,
                                 nsIURI *aURI,
                                 nsIURI *aInitialDocumentURI,
                                 nsIURI *aReferringURI,
                                 nsILoadGroup *aLoadGroup,
                                 nsLoadFlags aLoadFlags)
 {
   nsresult rv;
@@ -233,447 +228,153 @@ static nsresult NewImageChannel(nsIChann
       ++priority; // further reduce priority for background loads
 
     p->AdjustPriority(priority);
   }
 
   return NS_OK;
 }
 
-/* imgIRequest loadImage (in nsIURI aURI, in nsIURI initialDocumentURI, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */
-
-NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, 
-                                   nsIURI *aInitialDocumentURI,
-                                   nsIURI *aReferrerURI,
-                                   nsILoadGroup *aLoadGroup,
-                                   imgIDecoderObserver *aObserver,
-                                   nsISupports *aCX,
-                                   nsLoadFlags aLoadFlags,
-                                   nsISupports *cacheKey,
-                                   imgIRequest *aRequest,
-                                   imgIRequest **_retval)
+static PRUint32 SecondsFromPRTime(PRTime prTime)
 {
-  NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
-
-  // CreateNewProxyForRequest treats _retval as inout - null out
-  // to make sure the passed value doesn't affect the behavior of
-  // this method
-  *_retval = nsnull;
-
-  if (!aURI)
-    return NS_ERROR_NULL_POINTER;
-
-#if defined(PR_LOGGING)
-  nsCAutoString spec;
-  aURI->GetAsciiSpec(spec);
-  LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI", spec.get());
-#endif
-
-  // This is an owning reference that must be released.
-  imgRequest *request = nsnull;
-
-  nsresult rv;
-  nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
+  return PRUint32(PRInt64(prTime) / PRInt64(PR_USEC_PER_SEC));
+}
 
-  // Get the default load flags from the loadgroup (if possible)...
-  if (aLoadGroup) {
-    aLoadGroup->GetLoadFlags(&requestFlags);
-  }
-  //
-  // Merge the default load flags with those passed in via aLoadFlags.
-  // Currently, *only* the caching, validation and background load flags
-  // are merged...
-  //
-  // The flags in aLoadFlags take precidence over the default flags!
-  //
-  if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) {
-    // Override the default caching flags...
-    requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) |
-                   (aLoadFlags & LOAD_FLAGS_CACHE_MASK);
-  }
-  if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) {
-    // Override the default validation flags...
-    requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) |
-                   (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK);
-  }
-  if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
-    // Propagate background loading...
-    requestFlags |= nsIRequest::LOAD_BACKGROUND;
-  }
-
-  nsCOMPtr<nsICacheEntryDescriptor> entry;
-  PRBool bCanCacheRequest = PR_TRUE;
-  PRBool bHasExpired      = PR_FALSE;
-  PRBool bValidateRequest = PR_FALSE;
-
-  // XXX For now ignore the cache key. We will need it in the future
-  // for correctly dealing with image load requests that are a result
-  // of post data.
-  imgCache::Get(aURI, &bHasExpired,
-                &request, getter_AddRefs(entry)); // addrefs request
-
-  if (request && entry) {
+imgCacheEntry::imgCacheEntry(imgRequest *request, PRBool mustValidateIfExpired /* = PR_FALSE */)
+ : mRequest(request),
+   mDataSize(0),
+   mTouchedTime(SecondsFromPRTime(PR_Now())),
+   mExpiryTime(0),
+   mMustValidateIfExpired(mustValidateIfExpired),
+   mEvicted(PR_FALSE)
+{}
 
-    // request's null out their mCacheEntry when all proxy's are removed.
-    // If we are about to add a new one back, go ahead and re-set the cache
-    // entry so it can be used.
-    if (!request->mCacheEntry) {
-      request->mCacheEntry = entry;
-    }
-
-    // If the request's loadId is the same as the aCX, then it is ok to use
-    // this one because it has already been validated for this context.
-    //
-    // XXX: nsnull seems to be a 'special' key value that indicates that NO
-    //      validation is required.
-    //
-    void *key = (void*)aCX;
-    if (request->mLoadId != key) {
+void imgCacheEntry::TouchWithSize(PRInt32 diff)
+{
+  LOG_SCOPE(gImgLog, "imgCacheEntry::TouchWithSize");
 
-      // LOAD_BYPASS_CACHE - Always re-fetch
-      if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
-        // doom cache entry; be sure to break the reference cycle between the
-        // request and cache entry.  NOTE: the request might not own the cache
-        // entry at this point, so we explicitly Doom |entry| just in case.
-        entry->Doom();
-        entry = nsnull;
-        request->RemoveFromCache();
-        NS_RELEASE(request);
-      } else {
-        // Determine whether the cache entry must be revalidated...
-        bValidateRequest = RevalidateEntry(entry, requestFlags, bHasExpired);
-
-        PR_LOG(gImgLog, PR_LOG_DEBUG,
-               ("imgLoader::LoadImage validating cache entry. " 
-                "bValidateRequest = %d", bValidateRequest));
-      }
+  mTouchedTime = SecondsFromPRTime(PR_Now());
 
-    }
-#if defined(PR_LOGGING)
-    else if (!key) {
-      PR_LOG(gImgLog, PR_LOG_DEBUG,
-             ("imgLoader::LoadImage BYPASSING cache validation for %s " 
-              "because of NULL LoadID", spec.get()));
-    }
-#endif
+  if (!Evicted()) {
+    nsCOMPtr<nsIURI> uri;
+    mRequest->GetURI(getter_AddRefs(uri));
+    imgLoader::CacheEntriesChanged(uri, diff);
   }
+}
 
-  //
-  // Get the current thread...  This is used as a cacheId to prevent
-  // sharing requests which are being loaded across multiple threads...
-  //
-  void *cacheId = NS_GetCurrentThread();
-  if (request && !request->IsReusable(cacheId)) {
-    //
-    // The current request is still being loaded and lives on a different
-    // event queue.
-    //
-    // Since its event queue is NOT active, do not reuse this imgRequest !!
-    // Instead, force a new request to be created but DO NOT allow it to be
-    // cached!
-    //
-    PR_LOG(gImgLog, PR_LOG_DEBUG,
-           ("[this=%p] imgLoader::LoadImage -- DANGER!! Unable to use cached "
-            "imgRequest [request=%p]\n", this, request));
-
-    entry = nsnull;
-    NS_RELEASE(request);
-
-    bCanCacheRequest = PR_FALSE;
-  }
-
-  //
-  // Time to load the request... There are 3 possible cases:
-  // =======================================================
-  //   1. There is no cached request (ie. nothing was found in the cache).
-  //
-  //   2. There is a cached request that must be validated.
-  //
-  //   3. There is a valid cached request.
-  //
-  if (request && bValidateRequest) {
-    /* Case #2: the cache request cache must be revalidated. */
-    LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache hit| must validate");
+void imgCacheEntry::Touch(PRBool updateTime /* = PR_TRUE */)
+{
+  LOG_SCOPE(gImgLog, "imgCacheEntry::Touch");
 
-    // now we need to insert a new channel request object inbetween the real
-    // request and the proxy that basically delays loading the image until it
-    // gets a 304 or figures out that this needs to be a new request
-
-    if (request->mValidator) {
-      rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
-                                    requestFlags, aRequest, _retval);
-
-      if (*_retval)
-        request->mValidator->AddProxy(static_cast<imgRequestProxy*>(*_retval));
-
-      NS_RELEASE(request);
-      return rv;
-
-    } else {
-      nsCOMPtr<nsIChannel> newChannel;
-      rv = NewImageChannel(getter_AddRefs(newChannel),
-                           aURI,
-                           aInitialDocumentURI,
-                           aReferrerURI,
-                           aLoadGroup,
-                           requestFlags);
-      if (NS_FAILED(rv)) {
-        NS_RELEASE(request);
-        return NS_ERROR_FAILURE;
-      }
-
-      nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(newChannel));
+  if (updateTime)
+    mTouchedTime = SecondsFromPRTime(PR_Now());
 
-      if (cacheChan) {
-        // since this channel supports nsICachingChannel, we can ask it
-        // to only stream us data if the data comes off the net.
-        PRUint32 loadFlags;
-        if (NS_SUCCEEDED(newChannel->GetLoadFlags(&loadFlags)))
-            newChannel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
-
-      }
-      nsCOMPtr<imgIRequest> req;
-      rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
-                                    requestFlags, aRequest, getter_AddRefs(req));
-      if (NS_FAILED(rv)) {
-        NS_RELEASE(request);
-        return rv;
-      }
+  if (!Evicted()) {
+    nsCOMPtr<nsIURI> uri;
+    mRequest->GetURI(getter_AddRefs(uri));
+    imgLoader::CacheEntriesChanged(uri);
+  }
+}
 
-      imgCacheValidator *hvc = new imgCacheValidator(request, aCX);
-      if (!hvc) {
-        NS_RELEASE(request);
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-
-      NS_ADDREF(hvc);
-      request->mValidator = hvc;
-
-      hvc->AddProxy(static_cast<imgRequestProxy*>
-                               (static_cast<imgIRequest*>(req.get())));
-
-      rv = newChannel->AsyncOpen(static_cast<nsIStreamListener *>(hvc), nsnull);
-      if (NS_SUCCEEDED(rv))
-        NS_ADDREF(*_retval = req.get());
-
-      NS_RELEASE(hvc);
-
-      NS_RELEASE(request);
-
-      return rv;
-    }
-  } else if (!request) {
-    /* Case #1: no request from the cache.  do a new load */
-    LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|");
+imgCacheQueue::imgCacheQueue()
+ : mDirty(PR_FALSE),
+   mSize(0)
+{}
 
-    nsCOMPtr<nsIChannel> newChannel;
-    rv = NewImageChannel(getter_AddRefs(newChannel),
-                         aURI,
-                         aInitialDocumentURI,
-                         aReferrerURI,
-                         aLoadGroup,
-                         requestFlags);
-    if (NS_FAILED(rv))
-      return NS_ERROR_FAILURE;
-
-    NS_NEWXPCOM(request, imgRequest);
-    if (!request) return NS_ERROR_OUT_OF_MEMORY;
-
-    NS_ADDREF(request);
-
-    PR_LOG(gImgLog, PR_LOG_DEBUG,
-           ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request));
+void imgCacheQueue::UpdateSize(PRInt32 diff)
+{
+  mSize += diff;
+}
 
-    // Add the new request into the imgCache if its cachable...
-    if (bCanCacheRequest) {
-      imgCache::Put(aURI, request, getter_AddRefs(entry));
-    }
-
-    // Create a loadgroup for this new channel.  This way if the channel
-    // is redirected, we'll have a way to cancel the resulting channel.
-    nsCOMPtr<nsILoadGroup> loadGroup =
-        do_CreateInstance(NS_LOADGROUP_CONTRACTID);
-    newChannel->SetLoadGroup(loadGroup);
-
-    request->Init(aURI, loadGroup, entry, cacheId, aCX);
-
-    // create the proxy listener
-    ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request));
-    if (!pl) {
-      request->Cancel(NS_ERROR_OUT_OF_MEMORY);
-      NS_RELEASE(request);
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    NS_ADDREF(pl);
-
-    PR_LOG(gImgLog, PR_LOG_DEBUG,
-           ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
+PRUint32 imgCacheQueue::GetSize() const
+{
+  return mSize;
+}
 
-    nsresult openRes;
-    openRes = newChannel->AsyncOpen(static_cast<nsIStreamListener *>(pl), nsnull);
-
-    NS_RELEASE(pl);
+#include <algorithm>
 
-    if (NS_FAILED(openRes)) {
-      PR_LOG(gImgLog, PR_LOG_DEBUG,
-             ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
-              this, openRes));
-      request->Cancel(openRes);
-      NS_RELEASE(request);
-      return openRes;
-    }
-
-  } else {
-    /* Case #3: request found in cache.  use it */
-    // XXX: Should this be executed if an expired cache entry does not have a caching channel??
-    LOG_MSG_WITH_PARAM(gImgLog, 
-                       "imgLoader::LoadImage |cache hit|", "request", request);
-
-    // Update the request's LoadId
-    request->SetLoadId(aCX);
+void imgCacheQueue::Remove(imgCacheEntry *entry)
+{
+  queueContainer::iterator it = find(mQueue.begin(), mQueue.end(), entry);
+  if (it != mQueue.end()) {
+    mSize -= (*it)->GetDataSize();
+    mQueue.erase(it);
+    MarkDirty();
   }
-
-  LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request.");
-
-  rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
-                                requestFlags, aRequest, _retval);
-
-  imgRequestProxy *proxy = (imgRequestProxy *)*_retval;
-
-  // Note that it's OK to add here even if the request is done.  If it is,
-  // it'll send a OnStopRequest() to the proxy in NotifyProxyListener and the
-  // proxy will be removed from the loadgroup.
-  proxy->AddToLoadGroup();
-
-  // if we have to validate the request, then we will send the
-  // notifications later.
-  if (!bValidateRequest) {
-    request->NotifyProxyListener(proxy);
-  }
-
-  NS_RELEASE(request);
-
-  return rv;
 }
 
-/* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */
-NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval)
+void imgCacheQueue::Push(imgCacheEntry *entry)
 {
-  NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer");
-
-  // CreateNewProxyForRequest treats _retval as inout - null out
-  // to make sure the passed value doesn't affect the behavior of
-  // this method
-  *_retval = nsnull;
-
-  nsresult rv;
-  imgRequest *request = nsnull;
-
-  nsCOMPtr<nsIURI> uri;
-  channel->GetURI(getter_AddRefs(uri));
-
-  nsCOMPtr<nsICacheEntryDescriptor> entry;
-  PRBool bHasExpired;
-
-  imgCache::Get(uri, &bHasExpired, &request, getter_AddRefs(entry)); // addrefs request
-
-  nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
-
-  channel->GetLoadFlags(&requestFlags);
+  mSize += entry->GetDataSize();
 
-  if (request) {
-    PRBool bUseCacheCopy = PR_TRUE;
-
-    // LOAD_BYPASS_CACHE - Always re-fetch
-    if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
-      bUseCacheCopy = PR_FALSE;
-    }
-    else if (RevalidateEntry(entry, requestFlags, bHasExpired)) {
-      nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
-      if (cacheChan) {
-        cacheChan->IsFromCache(&bUseCacheCopy);
-      } else {
-        bUseCacheCopy = PR_FALSE;
-      }
-    }
-
-    if (!bUseCacheCopy) {
-      // doom cache entry; be sure to break the reference cycle between the
-      // request and cache entry.  NOTE: the request might not own the cache
-      // entry at this point, so we explicitly Doom |entry| just in case.
-      entry->Doom();
-      entry = nsnull;
-      request->RemoveFromCache();
-      NS_RELEASE(request);
-    }
-  }
+  nsRefPtr<imgCacheEntry> refptr(entry);
+  mQueue.push_back(refptr);
+  MarkDirty();
+}
 
-  nsCOMPtr<nsILoadGroup> loadGroup;
-  channel->GetLoadGroup(getter_AddRefs(loadGroup));
-
-  if (request) {
-    // we have this in our cache already.. cancel the current (document) load
-
-    channel->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED); // this should fire an OnStopRequest
+already_AddRefed<imgCacheEntry> imgCacheQueue::Pop()
+{
+  if (mQueue.empty())
+    return nsnull;
+  if (IsDirty())
+    Refresh();
 
-    *listener = nsnull; // give them back a null nsIStreamListener
-  } else {
-    //
-    // Get the current Thread...  This is used as a cacheId to prevent
-    // sharing requests which are being loaded across multiple threads...
-    //
-    nsIThread *thread = NS_GetCurrentThread();
-
-    NS_NEWXPCOM(request, imgRequest);
-    if (!request) return NS_ERROR_OUT_OF_MEMORY;
-
-    NS_ADDREF(request);
-
-    imgCache::Put(uri, request, getter_AddRefs(entry));
+  nsRefPtr<imgCacheEntry> entry = mQueue[0];
+  std::pop_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
+  mQueue.pop_back();
 
-    // XXX(darin):  I'm not sure that using the original URI is correct here.
-    // Perhaps we should use the same URI that indexes the cache?  Or, perhaps
-    // the cache should use the original URI?  See bug 89419.
-    nsCOMPtr<nsIURI> originalURI;
-    channel->GetOriginalURI(getter_AddRefs(originalURI));
-    request->Init(originalURI, channel, entry, thread, aCX);
-
-    ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request));
-    if (!pl) {
-      NS_RELEASE(request);
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    NS_ADDREF(pl);
+  mSize -= entry->GetDataSize();
+  imgCacheEntry *ret = entry;
+  NS_ADDREF(ret);
+  return ret;
+}
 
-    *listener = static_cast<nsIStreamListener*>(pl);
-    NS_ADDREF(*listener);
-
-    NS_RELEASE(pl);
-  }
-
-  // XXX: It looks like the wrong load flags are being passed in...
-  requestFlags &= 0xFFFF;
-
-  rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
-                                requestFlags, nsnull, _retval);
-  request->NotifyProxyListener(static_cast<imgRequestProxy*>(*_retval));
-
-  NS_RELEASE(request);
-
-  return rv;
+void imgCacheQueue::Refresh()
+{
+  std::make_heap(mQueue.begin(), mQueue.end(), imgLoader::CompareCacheEntries);
+  mDirty = PR_FALSE;
 }
 
+void imgCacheQueue::MarkDirty()
+{
+  mDirty = PR_TRUE;
+}
 
-nsresult
-imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
-                                    imgIDecoderObserver *aObserver,
-                                    nsLoadFlags aLoadFlags, imgIRequest *aProxyRequest,
-                                    imgIRequest **_retval)
+PRBool imgCacheQueue::IsDirty()
+{
+  return mDirty;
+}
+
+PRUint32 imgCacheQueue::GetNumElements() const
+{
+  return mQueue.size();
+}
+
+imgCacheQueue::iterator imgCacheQueue::begin()
+{
+  return mQueue.begin();
+}
+imgCacheQueue::const_iterator imgCacheQueue::begin() const
+{
+  return mQueue.begin();
+}
+
+imgCacheQueue::iterator imgCacheQueue::end()
+{
+  return mQueue.end();
+}
+imgCacheQueue::const_iterator imgCacheQueue::end() const
+{
+  return mQueue.end();
+}
+
+nsresult imgLoader::CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
+                                             imgIDecoderObserver *aObserver,
+                                             nsLoadFlags aLoadFlags, imgIRequest *aProxyRequest,
+                                             imgIRequest **_retval)
 {
   LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest", "imgRequest", aRequest);
 
   /* XXX If we move decoding onto separate threads, we should save off the
      calling thread here and pass it off to |proxyRequest| so that it call
      proxy calls to |aObserver|.
    */
 
@@ -693,26 +394,847 @@ imgLoader::CreateNewProxyForRequest(imgR
 
   // init adds itself to imgRequest's list of observers
   nsresult rv = proxyRequest->Init(aRequest, aLoadGroup, aObserver);
   if (NS_FAILED(rv)) {
     NS_RELEASE(proxyRequest);
     return rv;
   }
 
-  if (*_retval) {
-    (*_retval)->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED);
-    NS_RELEASE(*_retval);
-  }
   // transfer reference to caller
   *_retval = static_cast<imgIRequest*>(proxyRequest);
 
   return NS_OK;
 }
 
+class imgCacheObserver : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+private:
+  imgLoader mLoader;
+};
+
+NS_IMPL_ISUPPORTS1(imgCacheObserver, nsIObserver)
+
+NS_IMETHODIMP
+imgCacheObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
+{
+  if (strcmp(aTopic, "memory-pressure") == 0) {
+    mLoader.ClearCache(PR_FALSE);
+    mLoader.ClearCache(PR_TRUE);
+  } else if (strcmp(aTopic, "chrome-flush-skin-caches") == 0 ||
+             strcmp(aTopic, "chrome-flush-caches") == 0) {
+    mLoader.ClearCache(PR_TRUE);
+  }
+  return NS_OK;
+}
+
+class imgCacheExpirationTracker : public nsExpirationTracker<imgCacheEntry, 3>
+{
+  enum { TIMEOUT_SECONDS = 10 };
+public:
+  imgCacheExpirationTracker();
+
+protected:
+  void NotifyExpired(imgCacheEntry *entry);
+};
+
+imgCacheExpirationTracker::imgCacheExpirationTracker()
+ : nsExpirationTracker<imgCacheEntry, 3>(TIMEOUT_SECONDS * 1000)
+{}
+
+void imgCacheExpirationTracker::NotifyExpired(imgCacheEntry *entry)
+{
+  // We can be called multiple times on the same entry. Don't do work multiple
+  // times.
+  if (!entry->Evicted())
+    imgLoader::RemoveFromCache(entry);
+
+  imgLoader::VerifyCacheSizes();
+}
+
+imgCacheObserver *gCacheObserver;
+imgCacheExpirationTracker *gCacheTracker;
+
+imgLoader::imgCacheTable imgLoader::sCache;
+imgCacheQueue imgLoader::sCacheQueue;
+
+imgLoader::imgCacheTable imgLoader::sChromeCache;
+imgCacheQueue imgLoader::sChromeCacheQueue;
+
+PRFloat64 imgLoader::sCacheTimeWeight;
+PRUint32 imgLoader::sCacheMaxSize;
+
+NS_IMPL_ISUPPORTS4(imgLoader, imgILoader, nsIContentSniffer, imgICache, nsISupportsWeakReference)
+
+imgLoader::imgLoader()
+{
+  /* member initializers and constructor code */
+#ifdef DEBUG_pavlov
+  PrintImageDecoders();
+#endif
+}
+
+imgLoader::~imgLoader()
+{
+  /* destructor code */
+}
+
+void imgLoader::VerifyCacheSizes()
+{
+  if (!gCacheTracker)
+    return;
+
+  PRUint32 queuesize = sCacheQueue.GetNumElements() + sChromeCacheQueue.GetNumElements();
+  PRUint32 cachesize = sCache.Count() + sChromeCache.Count();
+  PRUint32 trackersize = 0;
+  for (nsExpirationTracker<imgCacheEntry, 3>::Iterator it(gCacheTracker); it.Next(); )
+    trackersize++;
+  NS_ASSERTION(queuesize == cachesize, "Queue and cache sizes out of sync!");
+  NS_ASSERTION(queuesize == trackersize, "Queue and tracker sizes out of sync!");
+}
+
+imgLoader::imgCacheTable & imgLoader::GetCache(nsIURI *aURI)
+{
+  PRBool chrome = PR_FALSE;
+  aURI->SchemeIs("chrome", &chrome);
+  if (chrome)
+    return sChromeCache;
+  else
+    return sCache;
+}
+
+imgCacheQueue & imgLoader::GetCacheQueue(nsIURI *aURI)
+{
+  PRBool chrome = PR_FALSE;
+  aURI->SchemeIs("chrome", &chrome);
+  if (chrome)
+    return sChromeCacheQueue;
+  else
+    return sCacheQueue;
+}
+
+nsresult imgLoader::InitCache()
+{
+  nsresult rv;
+  nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1", &rv);
+  if (NS_FAILED(rv))
+    return rv;
+  
+  gCacheObserver = new imgCacheObserver();
+  if (!gCacheObserver) 
+    return NS_ERROR_OUT_OF_MEMORY;
+  NS_ADDREF(gCacheObserver);
+
+  os->AddObserver(gCacheObserver, "memory-pressure", PR_FALSE);
+  os->AddObserver(gCacheObserver, "chrome-flush-skin-caches", PR_FALSE);
+  os->AddObserver(gCacheObserver, "chrome-flush-caches", PR_FALSE);
+
+  gCacheTracker = new imgCacheExpirationTracker();
+  if (!gCacheTracker)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  if (!sCache.Init())
+      return NS_ERROR_OUT_OF_MEMORY;
+  if (!sChromeCache.Init())
+      return NS_ERROR_OUT_OF_MEMORY;
+
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); 
+  if (NS_FAILED(rv))
+    return rv;
+
+  PRInt32 timeweight;
+  rv = prefs->GetIntPref("image.cache.timeweight", &timeweight);
+  if (NS_SUCCEEDED(rv))
+    sCacheTimeWeight = timeweight / 1000.0;
+  else
+    sCacheTimeWeight = 0.5;
+
+  PRInt32 cachesize;
+  rv = prefs->GetIntPref("image.cache.size", &cachesize);
+  if (NS_SUCCEEDED(rv))
+    sCacheMaxSize = cachesize;
+  else
+    sCacheMaxSize = 5 * 1024 * 1024;
+ 
+  return NS_OK;
+}
+
+/* void clearCache (in boolean chrome); */
+NS_IMETHODIMP imgLoader::ClearCache(PRBool chrome)
+{
+  if (chrome)
+    return ClearChromeImageCache();
+  else
+    return ClearImageCache();
+}
+
+/* void removeEntry(in nsIURI uri); */
+NS_IMETHODIMP imgLoader::RemoveEntry(nsIURI *uri)
+{
+  if (RemoveFromCache(uri))
+    return NS_OK;
+
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+/* imgIRequest findEntry(in nsIURI uri); */
+NS_IMETHODIMP imgLoader::FindEntryProperties(nsIURI *uri, nsIProperties **_retval)
+{
+  nsRefPtr<imgCacheEntry> entry;
+  nsCAutoString spec;
+  imgCacheTable &cache = GetCache(uri);
+
+  uri->GetSpec(spec);
+  *_retval = nsnull;
+
+  if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
+    if (gCacheTracker)
+      gCacheTracker->MarkUsed(entry);
+    nsRefPtr<imgRequest> request = getter_AddRefs(entry->GetRequest());
+    if (request) {
+      *_retval = request->Properties();
+      NS_ADDREF(*_retval);
+    }
+  }
+
+  return NS_OK;
+}
+
+void imgLoader::Shutdown()
+{
+  ClearChromeImageCache();
+  ClearImageCache();
+  NS_IF_RELEASE(gCacheObserver);
+  delete gCacheTracker;
+  gCacheTracker = nsnull;
+}
+
+nsresult imgLoader::ClearChromeImageCache()
+{
+  return EvictEntries(sChromeCache, sChromeCacheQueue);
+}
+
+nsresult imgLoader::ClearImageCache()
+{
+  return EvictEntries(sCache, sCacheQueue);
+}
+
+PRBool imgLoader::PutIntoCache(nsIURI *key, imgCacheEntry *entry)
+{
+  LOG_STATIC_FUNC(gImgLog, "imgLoader::PutIntoCache");
+
+  imgCacheTable &cache = GetCache(key);
+
+  nsCAutoString spec;
+  key->GetSpec(spec);
+
+  // Check to see if this request already exists in the cache and is being
+  // loaded on a different thread. If so, don't allow this entry to be added to
+  // the cache.
+  nsRefPtr<imgCacheEntry> tmpCacheEntry;
+  if (cache.Get(spec, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) {
+    PR_LOG(gImgLog, PR_LOG_DEBUG,
+           ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache", nsnull));
+    nsRefPtr<imgRequest> tmpRequest = getter_AddRefs(tmpCacheEntry->GetRequest());
+    void *cacheId = NS_GetCurrentThread();
+
+    if (!tmpRequest->IsReusable(cacheId))
+      return PR_FALSE;
+
+    if (gCacheTracker)
+      gCacheTracker->MarkUsed(tmpCacheEntry);
+
+    return PR_TRUE;
+  }
+
+  PR_LOG(gImgLog, PR_LOG_DEBUG,
+         ("[this=%p] imgLoader::PutIntoCache -- Element NOT already in the cache", nsnull));
+
+  if (!cache.Put(spec, entry))
+    return PR_FALSE;
+
+  imgCacheQueue &queue = GetCacheQueue(key);
+  queue.Push(entry);
+
+  if (gCacheTracker)
+    gCacheTracker->AddObject(entry);
+
+  CheckCacheLimits(cache, queue);
+
+  return PR_TRUE;
+}
+
+void imgLoader::CacheEntriesChanged(nsIURI *uri, PRInt32 sizediff /* = 0 */)
+{
+  imgCacheQueue &queue = GetCacheQueue(uri);
+  queue.MarkDirty();
+  queue.UpdateSize(sizediff);
+}
+
+void imgLoader::CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue)
+{
+  if (queue.GetNumElements() == 0)
+    NS_ASSERTION(queue.GetSize() == 0, 
+                 "imgLoader::CheckCacheLimits -- incorrect cache size");
+
+  // Remove entries from the cache until we're back under our desired size.
+  while (queue.GetSize() >= sCacheMaxSize) {
+    // Remove the first entry in the queue.
+    nsRefPtr<imgCacheEntry> entry(queue.Pop());
+
+    NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
+
+    if (entry)
+      RemoveFromCache(entry);
+  }
+}
+
+PRBool imgLoader::ValidateRequestWithNewChannel(imgRequest *request,
+                                                nsIURI *aURI,
+                                                nsIURI *aInitialDocumentURI,
+                                                nsIURI *aReferrerURI,
+                                                nsILoadGroup *aLoadGroup,
+                                                imgIDecoderObserver *aObserver,
+                                                nsISupports *aCX,
+                                                nsLoadFlags aLoadFlags,
+                                                imgIRequest *aExistingRequest,
+                                                imgIRequest **aProxyRequest)
+{
+  // now we need to insert a new channel request object inbetween the real
+  // request and the proxy that basically delays loading the image until it
+  // gets a 304 or figures out that this needs to be a new request
+
+  nsresult rv;
+
+  if (request->mValidator) {
+    rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
+                                  aLoadFlags, aExistingRequest, 
+                                  reinterpret_cast<imgIRequest **>(aProxyRequest));
+
+    if (*aProxyRequest)
+      request->mValidator->AddProxy(static_cast<imgRequestProxy*>(*aProxyRequest));
+
+    return NS_SUCCEEDED(rv);
+
+  } else {
+    nsCOMPtr<nsIChannel> newChannel;
+    rv = NewImageChannel(getter_AddRefs(newChannel),
+                         aURI,
+                         aInitialDocumentURI,
+                         aReferrerURI,
+                         aLoadGroup,
+                         aLoadFlags);
+    if (NS_FAILED(rv)) {
+      return PR_FALSE;
+    }
+
+    nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(newChannel));
+
+    if (cacheChan) {
+      // since this channel supports nsICachingChannel, we can ask it
+      // to only stream us data if the data comes off the net.
+      PRUint32 loadFlags;
+      if (NS_SUCCEEDED(newChannel->GetLoadFlags(&loadFlags)))
+        newChannel->SetLoadFlags(loadFlags | nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
+    }
+
+    nsCOMPtr<imgIRequest> req;
+    rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
+                                  aLoadFlags, aExistingRequest, getter_AddRefs(req));
+    if (NS_FAILED(rv)) {
+      return PR_FALSE;
+    }
+
+    imgCacheValidator *hvc = new imgCacheValidator(request, aCX);
+    if (!hvc) {
+      return PR_FALSE;
+    }
+
+    NS_ADDREF(hvc);
+    request->mValidator = hvc;
+
+    hvc->AddProxy(static_cast<imgRequestProxy*>
+                             (static_cast<imgIRequest*>(req.get())));
+
+    rv = newChannel->AsyncOpen(static_cast<nsIStreamListener *>(hvc), nsnull);
+    if (NS_SUCCEEDED(rv))
+      NS_ADDREF(*aProxyRequest = req.get());
+
+    NS_RELEASE(hvc);
+
+    return NS_SUCCEEDED(rv);
+  }
+}
+
+PRBool imgLoader::ValidateEntry(imgCacheEntry *aEntry,
+                                nsIURI *aURI,
+                                nsIURI *aInitialDocumentURI,
+                                nsIURI *aReferrerURI,
+                                nsILoadGroup *aLoadGroup,
+                                imgIDecoderObserver *aObserver,
+                                nsISupports *aCX,
+                                nsLoadFlags aLoadFlags,
+                                PRBool aCanMakeNewChannel,
+                                imgIRequest *aExistingRequest,
+                                imgIRequest **aProxyRequest)
+{
+  LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
+
+  PRBool hasExpired;
+  PRUint32 expirationTime = aEntry->GetExpiryTime();
+  if (expirationTime <= SecondsFromPRTime(PR_Now())) {
+    hasExpired = PR_TRUE;
+  } else {
+    hasExpired = PR_FALSE;
+  }
+
+  nsresult rv;
+
+  // Special treatment for file URLs - aEntry has expired if file has changed
+  nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(aURI));
+  if (fileUrl) {
+    PRUint32 lastModTime = aEntry->GetTouchedTime();
+
+    nsCOMPtr<nsIFile> theFile;
+    rv = fileUrl->GetFile(getter_AddRefs(theFile));
+    if (NS_SUCCEEDED(rv)) {
+      PRInt64 fileLastMod;
+      rv = theFile->GetLastModifiedTime(&fileLastMod);
+      if (NS_SUCCEEDED(rv)) {
+        // nsIFile uses millisec, NSPR usec
+        fileLastMod *= 1000;
+        hasExpired = SecondsFromPRTime((PRTime)fileLastMod) > lastModTime;
+      }
+    }
+  }
+
+  nsRefPtr<imgRequest> request(aEntry->GetRequest());
+
+  if (!request)
+    return PR_FALSE;
+
+  PRBool validateRequest = PR_FALSE;
+
+  // If the request's loadId is the same as the aCX, then it is ok to use
+  // this one because it has already been validated for this context.
+  //
+  // XXX: nsnull seems to be a 'special' key value that indicates that NO
+  //      validation is required.
+  //
+  void *key = (void *)aCX;
+  if (request->mLoadId != key) {
+    // Determine whether the cache aEntry must be revalidated...
+    validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired);
+
+    PR_LOG(gImgLog, PR_LOG_DEBUG,
+           ("imgLoader::ValidateEntry validating cache entry. " 
+            "validateRequest = %d", validateRequest));
+  }
+#if defined(PR_LOGGING)
+  else if (!key) {
+    nsCAutoString spec;
+    aURI->GetSpec(spec);
+
+    PR_LOG(gImgLog, PR_LOG_DEBUG,
+           ("imgLoader::ValidateEntry BYPASSING cache validation for %s " 
+            "because of NULL LoadID", spec.get()));
+  }
+#endif
+
+  //
+  // Get the current thread...  This is used as a cacheId to prevent
+  // sharing requests which are being loaded across multiple threads...
+  //
+  void *cacheId = NS_GetCurrentThread();
+  if (!request->IsReusable(cacheId)) {
+    //
+    // The current request is still being loaded and lives on a different
+    // event queue.
+    //
+    // Since its event queue is NOT active, do not reuse this imgRequest.
+    // PutIntoCache() will also ensure that we don't cache it.
+    //
+    PR_LOG(gImgLog, PR_LOG_DEBUG,
+           ("imgLoader::ValidateEntry -- DANGER!! Unable to use cached "
+            "imgRequest [request=%p]\n", address_of(request)));
+
+    return PR_FALSE;
+  }
+
+  if (validateRequest && aCanMakeNewChannel) {
+    LOG_SCOPE(gImgLog, "imgLoader::ValidateRequest |cache hit| must validate");
+
+    return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
+                                         aReferrerURI, aLoadGroup, aObserver,
+                                         aCX, aLoadFlags, aExistingRequest,
+                                         aProxyRequest);
+  } 
+
+  return !validateRequest;
+}
+
+
+PRBool imgLoader::RemoveFromCache(nsIURI *aKey)
+{
+  LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache uri");
+  if (!aKey) return PR_FALSE;
+
+  imgCacheTable &cache = GetCache(aKey);
+  imgCacheQueue &queue = GetCacheQueue(aKey);
+
+  nsCAutoString spec;
+  aKey->GetSpec(spec);
+
+  nsRefPtr<imgCacheEntry> entry;
+  if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
+    if (gCacheTracker)
+      gCacheTracker->RemoveObject(entry);
+    cache.Remove(spec);
+    queue.Remove(entry);
+    entry->SetEvicted(PR_TRUE);
+    return PR_TRUE;
+  }
+  else
+    return PR_FALSE;
+}
+
+PRBool imgLoader::RemoveFromCache(imgCacheEntry *entry)
+{
+  LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
+  PRBool ret = PR_FALSE;
+  nsRefPtr<imgRequest> request(getter_AddRefs(entry->GetRequest()));
+  if (request) {
+    nsCOMPtr<nsIURI> key;
+    if (NS_SUCCEEDED(request->GetURI(getter_AddRefs(key))) && key)
+      ret = RemoveFromCache(key);
+  }
+
+  return ret;
+}
+
+nsresult imgLoader::EvictEntries(imgCacheTable &aCacheToClear, imgCacheQueue &aQueueToClear)
+{
+  LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries");
+
+  // We have to make a temporary, since RemoveFromCache removes the element
+  // from the queue, invalidating iterators.
+  nsTArray<nsRefPtr<imgCacheEntry> > entries;
+  for (imgCacheQueue::iterator it = aQueueToClear.begin(); it != aQueueToClear.end(); ++it)
+    entries.AppendElement(*it);
+  
+  for (PRUint32  i = 0; i < entries.Length(); ++i)
+    if (!RemoveFromCache(entries[i]))
+      return NS_ERROR_FAILURE;
+
+  return NS_OK;
+}
+
+#define LOAD_FLAGS_CACHE_MASK    (nsIRequest::LOAD_BYPASS_CACHE | \
+                                  nsIRequest::LOAD_FROM_CACHE)
+
+#define LOAD_FLAGS_VALIDATE_MASK (nsIRequest::VALIDATE_ALWAYS |   \
+                                  nsIRequest::VALIDATE_NEVER |    \
+                                  nsIRequest::VALIDATE_ONCE_PER_SESSION)
+
+
+/* imgIRequest loadImage (in nsIURI aURI, in nsIURI initialDocumentURI, in nsILoadGroup aLoadGroup, in imgIDecoderObserver aObserver, in nsISupports aCX, in nsLoadFlags aLoadFlags, in nsISupports cacheKey, in imgIRequest aRequest); */
+
+NS_IMETHODIMP imgLoader::LoadImage(nsIURI *aURI, 
+                                   nsIURI *aInitialDocumentURI,
+                                   nsIURI *aReferrerURI,
+                                   nsILoadGroup *aLoadGroup,
+                                   imgIDecoderObserver *aObserver,
+                                   nsISupports *aCX,
+                                   nsLoadFlags aLoadFlags,
+                                   nsISupports *aCacheKey,
+                                   imgIRequest *aRequest,
+                                   imgIRequest **_retval)
+{
+  VerifyCacheSizes();
+
+  NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
+
+  if (!aURI)
+    return NS_ERROR_NULL_POINTER;
+
+#if defined(PR_LOGGING)
+  nsCAutoString spec;
+  aURI->GetSpec(spec);
+  LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI", spec.get());
+#endif
+
+  *_retval = nsnull;
+
+  nsRefPtr<imgRequest> request;
+
+  nsresult rv;
+  nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
+
+  // Get the default load flags from the loadgroup (if possible)...
+  if (aLoadGroup) {
+    aLoadGroup->GetLoadFlags(&requestFlags);
+  }
+  //
+  // Merge the default load flags with those passed in via aLoadFlags.
+  // Currently, *only* the caching, validation and background load flags
+  // are merged...
+  //
+  // The flags in aLoadFlags take precedence over the default flags!
+  //
+  if (aLoadFlags & LOAD_FLAGS_CACHE_MASK) {
+    // Override the default caching flags...
+    requestFlags = (requestFlags & ~LOAD_FLAGS_CACHE_MASK) |
+                   (aLoadFlags & LOAD_FLAGS_CACHE_MASK);
+  }
+  if (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK) {
+    // Override the default validation flags...
+    requestFlags = (requestFlags & ~LOAD_FLAGS_VALIDATE_MASK) |
+                   (aLoadFlags & LOAD_FLAGS_VALIDATE_MASK);
+  }
+  if (aLoadFlags & nsIRequest::LOAD_BACKGROUND) {
+    // Propagate background loading...
+    requestFlags |= nsIRequest::LOAD_BACKGROUND;
+  }
+
+  nsRefPtr<imgCacheEntry> entry;
+
+  // If we're bypassing the cache, we are guaranteed to want a new cache entry,
+  // since we're going to do a new load.
+  if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE) {
+    RemoveFromCache(aURI);
+  } else {
+    // Look in the cache for our URI, and then validate it.
+    // XXX For now ignore aCacheKey. We will need it in the future
+    // for correctly dealing with image load requests that are a result
+    // of post data.
+    imgCacheTable &cache = GetCache(aURI);
+    nsCAutoString spec;
+
+    aURI->GetSpec(spec);
+
+    if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
+      if (gCacheTracker)
+        gCacheTracker->MarkUsed(entry);
+
+      if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI, aLoadGroup, aObserver, aCX,
+                        requestFlags, PR_TRUE, aRequest, _retval)) {
+        request = getter_AddRefs(entry->GetRequest());
+
+        entry->Touch();
+#ifdef DEBUG_joe
+        printf("CACHEGET: %d %s %d\n", time(NULL), spec.get(), entry->GetDataSize());
+#endif
+      }
+      else
+        entry = nsnull;
+    }
+  }
+
+  // If we didn't get a cache hit, we need to load from the network.
+  if (!request) {
+    LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|");
+
+    nsCOMPtr<nsIChannel> newChannel;
+    rv = NewImageChannel(getter_AddRefs(newChannel),
+                         aURI,
+                         aInitialDocumentURI,
+                         aReferrerURI,
+                         aLoadGroup,
+                         requestFlags);
+    if (NS_FAILED(rv))
+      return NS_ERROR_FAILURE;
+
+    if (!NewRequestAndEntry(aURI, getter_AddRefs(request), getter_AddRefs(entry)))
+      return NS_ERROR_OUT_OF_MEMORY;
+
+    PR_LOG(gImgLog, PR_LOG_DEBUG,
+           ("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request.get()));
+
+    // Create a loadgroup for this new channel.  This way if the channel
+    // is redirected, we'll have a way to cancel the resulting channel.
+    nsCOMPtr<nsILoadGroup> loadGroup =
+        do_CreateInstance(NS_LOADGROUP_CONTRACTID);
+    newChannel->SetLoadGroup(loadGroup);
+
+    void *cacheId = NS_GetCurrentThread();
+    request->Init(aURI, loadGroup, entry, cacheId, aCX);
+
+    // create the proxy listener
+    ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request.get()));
+    if (!pl) {
+      request->Cancel(NS_ERROR_OUT_OF_MEMORY);
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    NS_ADDREF(pl);
+
+    PR_LOG(gImgLog, PR_LOG_DEBUG,
+           ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n", this));
+
+    nsresult openRes = newChannel->AsyncOpen(static_cast<nsIStreamListener *>(pl), nsnull);
+
+    NS_RELEASE(pl);
+
+    if (NS_FAILED(openRes)) {
+      PR_LOG(gImgLog, PR_LOG_DEBUG,
+             ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
+              this, openRes));
+      request->Cancel(openRes);
+      return openRes;
+    }
+
+    // Try to add the new request into the cache.
+    PutIntoCache(aURI, entry);
+
+  // If we did get a cache hit, use it.
+  } else {
+    // XXX: Should this be executed if an expired cache entry does not have a caching channel??
+    LOG_MSG_WITH_PARAM(gImgLog, 
+                       "imgLoader::LoadImage |cache hit|", "request", request);
+
+    // Update the request's LoadId
+    request->SetLoadId(aCX);
+  }
+
+  // If we didn't get a proxy when validating the cache entry, we need to create one.
+  if (!*_retval) {
+    LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request.");
+
+    rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
+                                  requestFlags, aRequest, _retval);
+    imgRequestProxy *proxy = static_cast<imgRequestProxy *>(*_retval);
+
+    // Note that it's OK to add here even if the request is done.  If it is,
+    // it'll send a OnStopRequest() to the proxy in NotifyProxyListener and the
+    // proxy will be removed from the loadgroup.
+    proxy->AddToLoadGroup();
+
+    request->NotifyProxyListener(proxy);
+
+    return rv;
+  }
+
+  NS_ASSERTION(*_retval, "imgLoader::LoadImage -- no return value");
+
+  return NS_OK;
+}
+
+/* imgIRequest loadImageWithChannel(in nsIChannel channel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener); */
+NS_IMETHODIMP imgLoader::LoadImageWithChannel(nsIChannel *channel, imgIDecoderObserver *aObserver, nsISupports *aCX, nsIStreamListener **listener, imgIRequest **_retval)
+{
+  NS_ASSERTION(channel, "imgLoader::LoadImageWithChannel -- NULL channel pointer");
+
+  nsresult rv;
+  nsRefPtr<imgRequest> request;
+
+  nsCOMPtr<nsIURI> uri;
+  channel->GetURI(getter_AddRefs(uri));
+
+  nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
+  channel->GetLoadFlags(&requestFlags);
+
+  nsRefPtr<imgCacheEntry> entry;
+
+  if (requestFlags & nsIRequest::LOAD_BYPASS_CACHE) {
+    RemoveFromCache(uri);
+  } else {
+    // Look in the cache for our URI, and then validate it.
+    // XXX For now ignore aCacheKey. We will need it in the future
+    // for correctly dealing with image load requests that are a result
+    // of post data.
+    imgCacheTable &cache = GetCache(uri);
+    nsCAutoString spec;
+
+    uri->GetSpec(spec);
+
+    if (cache.Get(spec, getter_AddRefs(entry)) && entry) {
+      if (gCacheTracker)
+        gCacheTracker->MarkUsed(entry);
+
+      // We don't want to kick off another network load. So we ask
+      // ValidateEntry to only do validation without creating a new proxy. If
+      // it says that the entry isn't valid any more, we'll only use the entry
+      // we're getting if the channel is loading from the cache anyways.
+      //
+      // XXX -- should this be changed? it's pretty much verbatim from the old
+      // code, but seems nonsensical.
+      if (ValidateEntry(entry, uri, nsnull, nsnull, nsnull, aObserver, aCX,
+                        requestFlags, PR_FALSE, nsnull, nsnull)) {
+        request = getter_AddRefs(entry->GetRequest());
+      } else {
+        nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
+        PRBool bUseCacheCopy;
+
+        if (cacheChan)
+          cacheChan->IsFromCache(&bUseCacheCopy);
+        else
+          bUseCacheCopy = PR_FALSE;
+
+        if (!bUseCacheCopy)
+          entry = nsnull;
+        else {
+          request = getter_AddRefs(entry->GetRequest());
+        }
+      }
+    }
+  }
+
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  channel->GetLoadGroup(getter_AddRefs(loadGroup));
+
+  if (request) {
+    // we have this in our cache already.. cancel the current (document) load
+
+    channel->Cancel(NS_IMAGELIB_ERROR_LOAD_ABORTED); // this should fire an OnStopRequest
+
+    *listener = nsnull; // give them back a null nsIStreamListener
+  } else {
+
+    // Get the current Thread...  This is used as a cacheId to prevent
+    // sharing requests which are being loaded across multiple threads...
+    nsIThread *thread = NS_GetCurrentThread();
+
+    NewRequestAndEntry(uri, getter_AddRefs(request), getter_AddRefs(entry));
+
+    // XXX(darin):  I'm not sure that using the original URI is correct here.
+    // Perhaps we should use the same URI that indexes the cache?  Or, perhaps
+    // the cache should use the original URI?  See bug 89419.
+    nsCOMPtr<nsIURI> originalURI;
+    channel->GetOriginalURI(getter_AddRefs(originalURI));
+    request->Init(originalURI, channel, entry, thread, aCX);
+
+    ProxyListener *pl = new ProxyListener(static_cast<nsIStreamListener *>(request.get()));
+    if (!pl)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(pl);
+
+    *listener = static_cast<nsIStreamListener*>(pl);
+    NS_ADDREF(*listener);
+
+    NS_RELEASE(pl);
+
+    // Try to add the new request into the cache.
+    PutIntoCache(uri, entry);
+  }
+
+  // XXX: It looks like the wrong load flags are being passed in...
+  requestFlags &= 0xFFFF;
+
+  rv = CreateNewProxyForRequest(request, loadGroup, aObserver,
+                                requestFlags, nsnull, _retval);
+  request->NotifyProxyListener(static_cast<imgRequestProxy*>(*_retval));
+
+  return rv;
+}
+
+
 NS_IMETHODIMP imgLoader::SupportImageWithMimeType(const char* aMimeType, PRBool *_retval)
 {
   *_retval = PR_FALSE;
   nsCOMPtr<nsIComponentRegistrar> reg;
   nsresult rv = NS_GetComponentRegistrar(getter_AddRefs(reg));
   if (NS_FAILED(rv))
     return rv;
   nsCAutoString mimeType(aMimeType);
@@ -872,41 +1394,34 @@ NS_IMETHODIMP ProxyListener::OnStopReque
 NS_IMETHODIMP ProxyListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
 {
   if (!mDestListener)
     return NS_ERROR_FAILURE;
 
   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset, count);
 }
 
-
-
-
-
 /**
  * http validate class.  check a channel for a 304
  */
 
 NS_IMPL_ISUPPORTS2(imgCacheValidator, nsIStreamListener, nsIRequestObserver)
 
+imgLoader imgCacheValidator::sImgLoader;
+
 imgCacheValidator::imgCacheValidator(imgRequest *request, void *aContext) :
+  mRequest(request),
   mContext(aContext)
-{
-  /* member initializers and constructor code */
-
-  mRequest = request;
-  NS_ADDREF(mRequest);
-}
+{}
 
 imgCacheValidator::~imgCacheValidator()
 {
   /* destructor code */
   if (mRequest) {
     mRequest->mValidator = nsnull;
-    NS_RELEASE(mRequest);
   }
 }
 
 void imgCacheValidator::AddProxy(imgRequestProxy *aProxy)
 {
   // aProxy needs to be in the loadgroup since we're validating from
   // the network.
   aProxy->AddToLoadGroup();
@@ -928,40 +1443,39 @@ NS_IMETHODIMP imgCacheValidator::OnStart
       for (PRInt32 i = count-1; i>=0; i--) {
         imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
         mRequest->NotifyProxyListener(proxy);
       }
 
       mRequest->SetLoadId(mContext);
       mRequest->mValidator = nsnull;
 
-      NS_RELEASE(mRequest); // assigns null
+      mRequest = nsnull;
 
       return NS_OK;
     }
   }
+
   // fun stuff.
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
-  nsCOMPtr<nsICacheEntryDescriptor> entry;
+  nsRefPtr<imgCacheEntry> entry;
   nsCOMPtr<nsIURI> uri;
 
   // Doom the old request's cache entry
   mRequest->RemoveFromCache();
 
   mRequest->GetURI(getter_AddRefs(uri));
 
   mRequest->mValidator = nsnull;
-  NS_RELEASE(mRequest); // assigns null
+  mRequest = nsnull;
 
   imgRequest *request;
-  NS_NEWXPCOM(request, imgRequest);
-  if (!request) return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(request);
 
-  imgCache::Put(uri, request, getter_AddRefs(entry));
+  if (!NewRequestAndEntry(uri, &request, getter_AddRefs(entry)))
+      return NS_ERROR_OUT_OF_MEMORY;
 
   // XXX(darin):  I'm not sure that using the original URI is correct here.
   // Perhaps we should use the same URI that indexes the cache?  Or, perhaps
   // the cache should use the original URI?  See bug 89419.
   nsCOMPtr<nsIURI> originalURI;
   channel->GetOriginalURI(getter_AddRefs(originalURI));
   request->Init(originalURI, channel, entry, NS_GetCurrentThread(), mContext);
 
@@ -975,16 +1489,19 @@ NS_IMETHODIMP imgCacheValidator::OnStart
 
   PRUint32 count = mProxies.Count();
   for (PRInt32 i = count-1; i>=0; i--) {
     imgRequestProxy *proxy = static_cast<imgRequestProxy *>(mProxies[i]);
     proxy->ChangeOwner(request);
     request->NotifyProxyListener(proxy);
   }
 
+  // Try to add the new request into the cache.
+  sImgLoader.PutIntoCache(uri, entry);
+
   NS_RELEASE(request);
 
   if (!mDestListener)
     return NS_OK;
 
   return mDestListener->OnStartRequest(aRequest, ctxt);
 }
 
--- a/modules/libpr0n/src/imgLoader.h
+++ b/modules/libpr0n/src/imgLoader.h
@@ -33,53 +33,272 @@
  * 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 "imgILoader.h"
+#include "imgICache.h"
+#include "nsWeakReference.h"
 #include "nsIContentSniffer.h"
+#include "nsRefPtrHashtable.h"
+#include "nsExpirationTracker.h"
+#include "nsAutoPtr.h"
+#include "prtypes.h"
+#include "imgRequest.h"
 
 #ifdef LOADER_THREADSAFE
 #include "prlock.h"
 #endif
 
 class imgRequest;
 class imgRequestProxy;
 class imgIRequest;
 class imgIDecoderObserver;
 class nsILoadGroup;
 
+class imgCacheEntry
+{
+public:
+  imgCacheEntry(imgRequest *request, PRBool mustValidateIfExpired = PR_FALSE);
+
+  nsrefcnt AddRef()
+  {
+    NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
+    NS_ASSERT_OWNINGTHREAD(imgCacheEntry);
+    ++mRefCnt;
+    NS_LOG_ADDREF(this, mRefCnt, "imgCacheEntry", sizeof(*this));
+    return mRefCnt;
+  }
+ 
+  nsrefcnt Release()
+  {
+    NS_PRECONDITION(0 != mRefCnt, "dup release");
+    NS_ASSERT_OWNINGTHREAD(imgCacheEntry);
+    --mRefCnt;
+    NS_LOG_RELEASE(this, mRefCnt, "imgCacheEntry");
+    if (mRefCnt == 0) {
+      mRefCnt = 1; /* stabilize */
+      delete this;
+      return 0;
+    }
+    return mRefCnt;                              
+  }
+
+  PRUint32 GetDataSize() const
+  {
+    return mDataSize;
+  }
+  void SetDataSize(PRUint32 aDataSize)
+  {
+    PRInt32 oldsize = mDataSize;
+    mDataSize = aDataSize;
+    TouchWithSize(mDataSize - oldsize);
+  }
+
+  PRInt32 GetTouchedTime() const
+  {
+    return mTouchedTime;
+  }
+  void SetTouchedTime(PRInt32 time)
+  {
+    mTouchedTime = time;
+    Touch(/* updateTime = */ PR_FALSE);
+  }
+
+  PRInt32 GetExpiryTime() const
+  {
+    return mExpiryTime;
+  }
+  void SetExpiryTime(PRInt32 aExpiryTime)
+  {
+    mExpiryTime = aExpiryTime;
+    Touch();
+  }
+
+  PRBool GetMustValidateIfExpired() const
+  {
+    return mMustValidateIfExpired;
+  }
+  void SetMustValidateIfExpired(PRBool aValidate)
+  {
+    mMustValidateIfExpired = aValidate;
+    Touch();
+  }
+
+  already_AddRefed<imgRequest> GetRequest() const
+  {
+    imgRequest *req = mRequest;
+    NS_ADDREF(req);
+    return req;
+  }
+
+  PRBool Evicted() const
+  {
+    return mEvicted;
+  }
+
+  nsExpirationState *GetExpirationState()
+  {
+    return &mExpirationState;
+  }
+
+private: // methods
+  friend class imgLoader;
+  friend class imgCacheQueue;
+  void Touch(PRBool updateTime = PR_TRUE);
+  void TouchWithSize(PRInt32 diff);
+  void SetEvicted(PRBool evict)
+  {
+    mEvicted = evict;
+  }
+
+private: // data
+  nsAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD
+
+  nsRefPtr<imgRequest> mRequest;
+  PRUint32 mDataSize;
+  PRInt32 mTouchedTime;
+  PRInt32 mExpiryTime;
+  nsExpirationState mExpirationState;
+  PRBool mMustValidateIfExpired;
+  PRBool mEvicted;
+};
+
+#include <vector>
+
 #define NS_IMGLOADER_CID \
 { /* 9f6a0d2e-1dd1-11b2-a5b8-951f13c846f7 */         \
      0x9f6a0d2e,                                     \
      0x1dd1,                                         \
      0x11b2,                                         \
     {0xa5, 0xb8, 0x95, 0x1f, 0x13, 0xc8, 0x46, 0xf7} \
 }
 
-class imgLoader : public imgILoader, public nsIContentSniffer
+class imgCacheQueue
+{
+public: 
+  imgCacheQueue();
+  void Remove(imgCacheEntry *);
+  void Push(imgCacheEntry *);
+  void MarkDirty();
+  PRBool IsDirty();
+  already_AddRefed<imgCacheEntry> Pop();
+  void Refresh();
+  PRUint32 GetSize() const;
+  void UpdateSize(PRInt32 diff);
+  PRUint32 GetNumElements() const;
+  typedef std::vector<nsRefPtr<imgCacheEntry> > queueContainer;  
+  typedef queueContainer::iterator iterator;
+  typedef queueContainer::const_iterator const_iterator;
+
+  iterator begin();
+  const_iterator begin() const;
+  iterator end();
+  const_iterator end() const;
+
+private:
+  queueContainer mQueue;
+  PRBool mDirty;
+  PRUint32 mSize;
+};
+
+class imgLoader : public imgILoader,
+                  public nsIContentSniffer,
+                  public imgICache,
+                  public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGILOADER
   NS_DECL_NSICONTENTSNIFFER
+  NS_DECL_IMGICACHE
 
   imgLoader();
   virtual ~imgLoader();
 
   static nsresult GetMimeTypeFromContent(const char* aContents, PRUint32 aLength, nsACString& aContentType);
 
-private:
+  static void Shutdown(); // for use by the factory
+
+  static nsresult ClearChromeImageCache();
+  static nsresult ClearImageCache();
+
+  static nsresult InitCache();
+
+  static PRBool RemoveFromCache(nsIURI *aKey);
+  static PRBool RemoveFromCache(imgCacheEntry *entry);
+
+  static PRBool PutIntoCache(nsIURI *key, imgCacheEntry *entry);
+
+  // Returns true if |one| is less than |two|
+  // This mixes units in the worst way, but provides reasonable results.
+  inline static bool CompareCacheEntries(const nsRefPtr<imgCacheEntry> &one,
+                                  const nsRefPtr<imgCacheEntry> &two)
+  {
+    if (!one)
+      return false;
+    if (!two)
+      return true;
+
+    const PRFloat64 sizeweight = 1.0 - sCacheTimeWeight;
+    PRInt32 diffsize = PRInt32(two->GetDataSize()) - PRInt32(one->GetDataSize());
+    PRInt32 difftime = one->GetTouchedTime() - two->GetTouchedTime();
+    return difftime * sCacheTimeWeight + diffsize * sizeweight < 0;
+  }
+
+  static void VerifyCacheSizes();
+
+private: // methods
+
+
+  PRBool ValidateEntry(imgCacheEntry *aEntry, nsIURI *aKey,
+                       nsIURI *aInitialDocumentURI, nsIURI *aReferrerURI, 
+                       nsILoadGroup *aLoadGroup,
+                       imgIDecoderObserver *aObserver, nsISupports *aCX,
+                       nsLoadFlags aLoadFlags, PRBool aCanMakeNewChannel,
+                       imgIRequest *aExistingRequest,
+                       imgIRequest **aProxyRequest);
+  PRBool ValidateRequestWithNewChannel(imgRequest *request, nsIURI *aURI,
+                                       nsIURI *aInitialDocumentURI,
+                                       nsIURI *aReferrerURI,
+                                       nsILoadGroup *aLoadGroup,
+                                       imgIDecoderObserver *aObserver,
+                                       nsISupports *aCX, nsLoadFlags aLoadFlags,
+                                       imgIRequest *aExistingRequest,
+                                       imgIRequest **aProxyRequest);
+
   nsresult CreateNewProxyForRequest(imgRequest *aRequest, nsILoadGroup *aLoadGroup,
                                     imgIDecoderObserver *aObserver,
                                     nsLoadFlags aLoadFlags, imgIRequest *aRequestProxy,
                                     imgIRequest **_retval);
+
+
+  typedef nsRefPtrHashtable<nsCStringHashKey, imgCacheEntry> imgCacheTable;
+
+  static nsresult EvictEntries(imgCacheTable &aCacheToClear, imgCacheQueue &aQueueToClear);
+
+  static imgCacheTable &GetCache(nsIURI *aURI);
+  static imgCacheQueue &GetCacheQueue(nsIURI *aURI);
+  static void CacheEntriesChanged(nsIURI *aURI, PRInt32 sizediff = 0);
+  static void CheckCacheLimits(imgCacheTable &cache, imgCacheQueue &queue);
+
+private: // data
+  friend class imgCacheEntry;
+
+  static imgCacheTable sCache;
+  static imgCacheQueue sCacheQueue;
+
+  static imgCacheTable sChromeCache;
+  static imgCacheQueue sChromeCacheQueue;
+  static PRFloat64 sCacheTimeWeight;
+  static PRUint32 sCacheMaxSize;
 };
 
 
 
 /**
  * proxy stream listener class used to handle multipart/x-mixed-replace
  */
 
@@ -119,13 +338,15 @@ public:
   /* additional members */
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
 
 private:
   nsCOMPtr<nsIStreamListener> mDestListener;
 
-  imgRequest *mRequest;
+  nsRefPtr<imgRequest> mRequest;
   nsCOMArray<imgIRequest> mProxies;
 
   void *mContext;
+
+  static imgLoader sImgLoader;
 };
--- a/modules/libpr0n/src/imgRequest.cpp
+++ b/modules/libpr0n/src/imgRequest.cpp
@@ -58,16 +58,18 @@
 #include "nsIHttpChannel.h"
 
 #include "nsIComponentManager.h"
 #include "nsIProxyObjectManager.h"
 #include "nsIServiceManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIScriptSecurityManager.h"
 
+#include "nsICacheVisitor.h"
+
 #include "nsString.h"
 #include "nsXPIDLString.h"
 #include "plstr.h" // PL_strcasestr(...)
 
 #if defined(PR_LOGGING)
 PRLogModuleInfo *gImgLog = PR_NewLogModule("imgRequest");
 #endif
 
@@ -87,17 +89,17 @@ imgRequest::imgRequest() :
 
 imgRequest::~imgRequest()
 {
   /* destructor code */
 }
 
 nsresult imgRequest::Init(nsIURI *aURI,
                           nsIRequest *aRequest,
-                          nsICacheEntryDescriptor *aCacheEntry,
+                          imgCacheEntry *aCacheEntry,
                           void *aCacheId,
                           void *aLoadId)
 {
   LOG_FUNC(gImgLog, "imgRequest::Init");
 
   NS_ASSERTION(!mImage, "Multiple calls to init");
   NS_ASSERTION(aURI, "No uri");
   NS_ASSERTION(aRequest, "No request");
@@ -331,17 +333,17 @@ nsresult imgRequest::GetSecurityInfo(nsI
   return NS_OK;
 }
 
 void imgRequest::RemoveFromCache()
 {
   LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
 
   if (mCacheEntry) {
-    mCacheEntry->Doom();
+    imgLoader::RemoveFromCache(mURI);
     mCacheEntry = nsnull;
   }
 }
 
 PRBool imgRequest::HaveProxyWithObserver(imgRequestProxy* aProxyToIgnore) const
 {
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   imgRequestProxy* proxy;
@@ -518,23 +520,29 @@ NS_IMETHODIMP imgRequest::OnStopFrame(im
   NS_ASSERTION(frame, "imgRequest::OnStopFrame called with NULL frame");
   if (!frame) return NS_ERROR_UNEXPECTED;
 
   LOG_SCOPE(gImgLog, "imgRequest::OnStopFrame");
 
   mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
 
   if (mCacheEntry) {
-    PRUint32 cacheSize = 0;
-    mCacheEntry->GetDataSize(&cacheSize);
+    PRUint32 cacheSize = mCacheEntry->GetDataSize();
 
     PRUint32 imageSize = 0;
     frame->GetImageDataLength(&imageSize);
 
     mCacheEntry->SetDataSize(cacheSize + imageSize);
+
+#ifdef DEBUG_joe
+    nsCAutoString url;
+    mURI->GetSpec(url);
+
+    printf("CACHEPUT: %d %s %d\n", time(NULL), url.get(), cacheSize + imageSize);
+#endif
   }
 
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
     iter.GetNext()->OnStopFrame(frame);
   }
 
   return NS_OK;
@@ -642,17 +650,17 @@ NS_IMETHODIMP imgRequest::OnStartRequest
       if (cacheToken) {
         nsCOMPtr<nsICacheEntryInfo> entryDesc(do_QueryInterface(cacheToken));
         if (entryDesc) {
           PRUint32 expiration;
           /* get the expiration time from the caching channel's token */
           entryDesc->GetExpirationTime(&expiration);
 
           /* set the expiration time on our entry */
-          mCacheEntry->SetExpirationTime(expiration);
+          mCacheEntry->SetExpiryTime(expiration);
         }
       }
     }
     //
     // Determine whether the cache entry must be revalidated when it expires.
     // If so, then the cache entry must *not* be used during HISTORY loads if
     // it has expired.
     //
@@ -673,19 +681,17 @@ NS_IMETHODIMP imgRequest::OnStartRequest
 
         rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Cache-Control"),
                                             cacheHeader);
         if (PL_strcasestr(cacheHeader.get(), "must-revalidate")) {
           bMustRevalidate = PR_TRUE;
         }
       }
 
-      if (bMustRevalidate) {
-        mCacheEntry->SetMetaDataElement("MustValidateIfExpired", "true");
-      }
+      mCacheEntry->SetMustValidateIfExpired(bMustRevalidate);
     }
   }
 
 
   // Shouldn't we be dead already if this gets hit?  Probably multipart/x-mixed-replace...
   if (mObservers.IsEmpty()) {
     this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
   }
@@ -748,19 +754,16 @@ NS_IMETHODIMP imgRequest::OnStopRequest(
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
     iter.GetNext()->OnStopRequest(aRequest, ctxt, status, mHadLastPart);
   }
 
   return NS_OK;
 }
 
-
-
-
 /* prototype for this defined below */
 static NS_METHOD sniff_mimetype_callback(nsIInputStream* in, void* closure, const char* fromRawSegment,
                                          PRUint32 toOffset, PRUint32 count, PRUint32 *writeCount);
 
 
 /** nsIStreamListener methods **/
 
 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
--- a/modules/libpr0n/src/imgRequest.h
+++ b/modules/libpr0n/src/imgRequest.h
@@ -41,33 +41,33 @@
 #define imgRequest_h__
 
 #include "imgILoad.h"
 
 #include "imgIContainer.h"
 #include "imgIDecoder.h"
 #include "imgIDecoderObserver.h"
 
-#include "nsICacheEntryDescriptor.h"
 #include "nsIContentSniffer.h"
 #include "nsIRequest.h"
 #include "nsIProperties.h"
 #include "nsIStreamListener.h"
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
 
 #include "nsCategoryCache.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTObserverArray.h"
 #include "nsWeakReference.h"
 
 class imgCacheValidator;
 
 class imgRequestProxy;
+class imgCacheEntry;
 
 enum {
   onStartRequest   = PR_BIT(0),
   onStartDecode    = PR_BIT(1),
   onStartContainer = PR_BIT(2),
   onStopContainer  = PR_BIT(3),
   onStopDecode     = PR_BIT(4),
   onStopRequest    = PR_BIT(5)
@@ -81,17 +81,17 @@ class imgRequest : public imgILoad,
 public:
   imgRequest();
   virtual ~imgRequest();
 
   NS_DECL_ISUPPORTS
 
   nsresult Init(nsIURI *aURI,
                 nsIRequest *aRequest,
-                nsICacheEntryDescriptor *aCacheEntry,
+                imgCacheEntry *aCacheEntry,
                 void *aCacheId,
                 void *aLoadId);
 
   // Callers must call NotifyProxyListener later.
   nsresult AddProxy(imgRequestProxy *proxy);
 
   // aNotify==PR_FALSE still sends OnStopRequest.
   nsresult RemoveProxy(imgRequestProxy *proxy, nsresult aStatus, PRBool aNotify);
@@ -104,20 +104,20 @@ public:
   // being made...
   PRBool IsReusable(void *aCacheId) { return !mLoading || (aCacheId == mCacheId); }
 
   // get the current or last network status from our
   // internal nsIChannel.
   nsresult GetNetworkStatus();
 
 private:
+  friend class imgCacheEntry;
   friend class imgRequestProxy;
   friend class imgLoader;
   friend class imgCacheValidator;
-  friend class imgCache;
 
   inline void SetLoadId(void *aLoadId) {
     mLoadId = aLoadId;
     mLoadTime = PR_Now();
   }
   inline PRUint32 GetImageStatus() const { return mImageStatus; }
   inline nsresult GetResultFromImageStatus(PRUint32 aStatus) const;
   void Cancel(nsresult aStatus);
@@ -165,17 +165,17 @@ private:
   PRPackedBool mLoading;
   PRPackedBool mProcessing;
   PRPackedBool mHadLastPart;
   PRUint32 mNetworkStatus;
   PRUint32 mImageStatus;
   PRUint32 mState;
   nsCString mContentType;
 
-  nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry; /* we hold on to this to this so long as we have observers */
+  nsRefPtr<imgCacheEntry> mCacheEntry; /* we hold on to this to this so long as we have observers */
 
   void *mCacheId;
 
   void *mLoadId;
   PRTime mLoadTime;
 
   imgCacheValidator *mValidator;
   PRBool   mIsMultiPartChannel;
--- a/modules/libpr0n/test/Makefile.in
+++ b/modules/libpr0n/test/Makefile.in
@@ -44,16 +44,16 @@ relativesrcdir  = modules/libpr0n/test
 include $(DEPTH)/config/autoconf.mk
 
 # Module name for xpcshell tests.
 MODULE		= test_libpr0n
 
 XPCSHELL_TESTS  = unit
 
 #ifdef MOZ_MOCHITEST
-#DIRS            += mochitest
+DIRS            += mochitest
 #endif
 
 include $(topsrcdir)/config/rules.mk
 
 # Note: Invoke any additional (non-xpcshell) test programs here.
 check::
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -2617,8 +2617,15 @@ pref("signon.SignonFileName3",          
 pref("signon.autofillForms",                true); 
 pref("signon.debug",                        false); // logs to Error Console
 
 // Zoom prefs
 pref("browser.zoom.full", false);
 pref("zoom.minPercent", 30);
 pref("zoom.maxPercent", 300);
 pref("toolkit.zoomManager.zoomValues", ".3,.5,.67,.8,.9,1,1.1,1.2,1.33,1.5,1.7,2,2.4,3");
+
+// Image cache prefs
+// The maximum size, in bytes, of the decoded images we cache
+pref("image.cache.size", 5242880);
+// A weight, from 0-1000, to place on time when comparing to size.
+// Size is given a weight of 1000 - timeweight.
+pref("image.cache.timeweight", 500);