Bug 542222 - Reduce recursion in netwerk makefiles. r=biesi
authorMitchell Field <mitchell.field@live.com.au>
Wed, 02 Jun 2010 01:56:00 -0400
changeset 43113 d8dc49d5bd60
parent 43112 28086cf6ede8
child 43114 01f8d7c7654d
push id13625
push userCallek@gmail.com
push date2010-06-05 02:02 +0000
treeherdermozilla-central@63b89b311461 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi
bugs542222
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 542222 - Reduce recursion in netwerk makefiles. r=biesi
netwerk/build/Makefile.in
netwerk/cache/Makefile.in
netwerk/cache/nsCache.cpp
netwerk/cache/nsCache.h
netwerk/cache/nsCacheDevice.h
netwerk/cache/nsCacheEntry.cpp
netwerk/cache/nsCacheEntry.h
netwerk/cache/nsCacheEntryDescriptor.cpp
netwerk/cache/nsCacheEntryDescriptor.h
netwerk/cache/nsCacheMetaData.cpp
netwerk/cache/nsCacheMetaData.h
netwerk/cache/nsCacheRequest.h
netwerk/cache/nsCacheService.cpp
netwerk/cache/nsCacheService.h
netwerk/cache/nsCacheSession.cpp
netwerk/cache/nsCacheSession.h
netwerk/cache/nsDeleteDir.cpp
netwerk/cache/nsDeleteDir.h
netwerk/cache/nsDiskCache.h
netwerk/cache/nsDiskCacheBinding.cpp
netwerk/cache/nsDiskCacheBinding.h
netwerk/cache/nsDiskCacheBlockFile.cpp
netwerk/cache/nsDiskCacheBlockFile.h
netwerk/cache/nsDiskCacheDevice.cpp
netwerk/cache/nsDiskCacheDevice.h
netwerk/cache/nsDiskCacheDeviceSQL.cpp
netwerk/cache/nsDiskCacheDeviceSQL.h
netwerk/cache/nsDiskCacheEntry.cpp
netwerk/cache/nsDiskCacheEntry.h
netwerk/cache/nsDiskCacheMap.cpp
netwerk/cache/nsDiskCacheMap.h
netwerk/cache/nsDiskCacheStreams.cpp
netwerk/cache/nsDiskCacheStreams.h
netwerk/cache/nsICache.idl
netwerk/cache/nsICacheEntryDescriptor.idl
netwerk/cache/nsICacheListener.idl
netwerk/cache/nsICacheService.idl
netwerk/cache/nsICacheSession.idl
netwerk/cache/nsICacheVisitor.idl
netwerk/cache/nsMemoryCacheDevice.cpp
netwerk/cache/nsMemoryCacheDevice.h
netwerk/cache/public/Makefile.in
netwerk/cache/public/nsICache.idl
netwerk/cache/public/nsICacheEntryDescriptor.idl
netwerk/cache/public/nsICacheListener.idl
netwerk/cache/public/nsICacheService.idl
netwerk/cache/public/nsICacheSession.idl
netwerk/cache/public/nsICacheVisitor.idl
netwerk/cache/src/Makefile.in
netwerk/cache/src/nsCache.cpp
netwerk/cache/src/nsCache.h
netwerk/cache/src/nsCacheDevice.h
netwerk/cache/src/nsCacheEntry.cpp
netwerk/cache/src/nsCacheEntry.h
netwerk/cache/src/nsCacheEntryDescriptor.cpp
netwerk/cache/src/nsCacheEntryDescriptor.h
netwerk/cache/src/nsCacheMetaData.cpp
netwerk/cache/src/nsCacheMetaData.h
netwerk/cache/src/nsCacheRequest.h
netwerk/cache/src/nsCacheService.cpp
netwerk/cache/src/nsCacheService.h
netwerk/cache/src/nsCacheSession.cpp
netwerk/cache/src/nsCacheSession.h
netwerk/cache/src/nsDeleteDir.cpp
netwerk/cache/src/nsDeleteDir.h
netwerk/cache/src/nsDiskCache.h
netwerk/cache/src/nsDiskCacheBinding.cpp
netwerk/cache/src/nsDiskCacheBinding.h
netwerk/cache/src/nsDiskCacheBlockFile.cpp
netwerk/cache/src/nsDiskCacheBlockFile.h
netwerk/cache/src/nsDiskCacheDevice.cpp
netwerk/cache/src/nsDiskCacheDevice.h
netwerk/cache/src/nsDiskCacheDeviceSQL.cpp
netwerk/cache/src/nsDiskCacheDeviceSQL.h
netwerk/cache/src/nsDiskCacheEntry.cpp
netwerk/cache/src/nsDiskCacheEntry.h
netwerk/cache/src/nsDiskCacheMap.cpp
netwerk/cache/src/nsDiskCacheMap.h
netwerk/cache/src/nsDiskCacheStreams.cpp
netwerk/cache/src/nsDiskCacheStreams.h
netwerk/cache/src/nsMemoryCacheDevice.cpp
netwerk/cache/src/nsMemoryCacheDevice.h
netwerk/cookie/Makefile.in
netwerk/cookie/nsCookie.cpp
netwerk/cookie/nsCookie.h
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
netwerk/cookie/nsICookie.idl
netwerk/cookie/nsICookie2.idl
netwerk/cookie/nsICookieManager.idl
netwerk/cookie/nsICookieManager2.idl
netwerk/cookie/nsICookiePermission.idl
netwerk/cookie/nsICookieService.idl
netwerk/cookie/public/Makefile.in
netwerk/cookie/public/nsICookie.idl
netwerk/cookie/public/nsICookie2.idl
netwerk/cookie/public/nsICookieManager.idl
netwerk/cookie/public/nsICookieManager2.idl
netwerk/cookie/public/nsICookiePermission.idl
netwerk/cookie/public/nsICookieService.idl
netwerk/cookie/src/Makefile.in
netwerk/cookie/src/nsCookie.cpp
netwerk/cookie/src/nsCookie.h
netwerk/cookie/src/nsCookieService.cpp
netwerk/cookie/src/nsCookieService.h
netwerk/dns/Makefile.in
netwerk/dns/effective_tld_names.dat
netwerk/dns/nameprep.c
netwerk/dns/nameprep_template.c
netwerk/dns/nameprepdata.c
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsDNSService2.h
netwerk/dns/nsEffectiveTLDService.cpp
netwerk/dns/nsEffectiveTLDService.h
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
netwerk/dns/nsIDNKitInterface.h
netwerk/dns/nsIDNSListener.idl
netwerk/dns/nsIDNSRecord.idl
netwerk/dns/nsIDNSRequest.idl
netwerk/dns/nsIDNSService.idl
netwerk/dns/nsIDNService.cpp
netwerk/dns/nsIDNService.h
netwerk/dns/nsIEffectiveTLDService.idl
netwerk/dns/nsIIDNService.idl
netwerk/dns/nsPIDNSService.idl
netwerk/dns/prepare_tlds.py
netwerk/dns/public/Makefile.in
netwerk/dns/public/nsIDNSListener.idl
netwerk/dns/public/nsIDNSRecord.idl
netwerk/dns/public/nsIDNSRequest.idl
netwerk/dns/public/nsIDNSService.idl
netwerk/dns/public/nsIEffectiveTLDService.idl
netwerk/dns/public/nsIIDNService.idl
netwerk/dns/public/nsPIDNSService.idl
netwerk/dns/punycode.c
netwerk/dns/punycode.h
netwerk/dns/race.c
netwerk/dns/src/Makefile.in
netwerk/dns/src/effective_tld_names.dat
netwerk/dns/src/nameprep.c
netwerk/dns/src/nameprep_template.c
netwerk/dns/src/nameprepdata.c
netwerk/dns/src/nsDNSService2.cpp
netwerk/dns/src/nsDNSService2.h
netwerk/dns/src/nsEffectiveTLDService.cpp
netwerk/dns/src/nsEffectiveTLDService.h
netwerk/dns/src/nsHostResolver.cpp
netwerk/dns/src/nsHostResolver.h
netwerk/dns/src/nsIDNKitInterface.h
netwerk/dns/src/nsIDNService.cpp
netwerk/dns/src/nsIDNService.h
netwerk/dns/src/prepare_tlds.py
netwerk/dns/src/punycode.c
netwerk/dns/src/punycode.h
netwerk/dns/src/race.c
netwerk/mime/Makefile.in
netwerk/mime/nsIMIMEHeaderParam.idl
netwerk/mime/nsIMIMEInfo.idl
netwerk/mime/nsIMIMEService.idl
netwerk/mime/nsMIMEHeaderParamImpl.cpp
netwerk/mime/nsMIMEHeaderParamImpl.h
netwerk/mime/nsMimeTypes.h
netwerk/mime/public/Makefile.in
netwerk/mime/public/nsIMIMEHeaderParam.idl
netwerk/mime/public/nsIMIMEInfo.idl
netwerk/mime/public/nsIMIMEService.idl
netwerk/mime/public/nsMimeTypes.h
netwerk/mime/src/Makefile.in
netwerk/mime/src/nsMIMEHeaderParamImpl.cpp
netwerk/mime/src/nsMIMEHeaderParamImpl.h
netwerk/protocol/about/Makefile.in
netwerk/protocol/about/nsAboutBlank.cpp
netwerk/protocol/about/nsAboutBlank.h
netwerk/protocol/about/nsAboutBloat.cpp
netwerk/protocol/about/nsAboutBloat.h
netwerk/protocol/about/nsAboutCache.cpp
netwerk/protocol/about/nsAboutCache.h
netwerk/protocol/about/nsAboutCacheEntry.cpp
netwerk/protocol/about/nsAboutCacheEntry.h
netwerk/protocol/about/nsAboutProtocolHandler.cpp
netwerk/protocol/about/nsAboutProtocolHandler.h
netwerk/protocol/about/nsAboutProtocolUtils.h
netwerk/protocol/about/nsIAboutModule.idl
netwerk/protocol/about/public/Makefile.in
netwerk/protocol/about/public/nsAboutProtocolUtils.h
netwerk/protocol/about/public/nsIAboutModule.idl
netwerk/protocol/about/src/Makefile.in
netwerk/protocol/about/src/nsAboutBlank.cpp
netwerk/protocol/about/src/nsAboutBlank.h
netwerk/protocol/about/src/nsAboutBloat.cpp
netwerk/protocol/about/src/nsAboutBloat.h
netwerk/protocol/about/src/nsAboutCache.cpp
netwerk/protocol/about/src/nsAboutCache.h
netwerk/protocol/about/src/nsAboutCacheEntry.cpp
netwerk/protocol/about/src/nsAboutCacheEntry.h
netwerk/protocol/about/src/nsAboutProtocolHandler.cpp
netwerk/protocol/about/src/nsAboutProtocolHandler.h
netwerk/protocol/data/Makefile.in
netwerk/protocol/data/nsDataChannel.cpp
netwerk/protocol/data/nsDataChannel.h
netwerk/protocol/data/nsDataHandler.cpp
netwerk/protocol/data/nsDataHandler.h
netwerk/protocol/data/nsDataModule.cpp
netwerk/protocol/data/src/Makefile.in
netwerk/protocol/data/src/nsDataChannel.cpp
netwerk/protocol/data/src/nsDataChannel.h
netwerk/protocol/data/src/nsDataHandler.cpp
netwerk/protocol/data/src/nsDataHandler.h
netwerk/protocol/data/src/nsDataModule.cpp
netwerk/protocol/file/Makefile.in
netwerk/protocol/file/nsFileChannel.cpp
netwerk/protocol/file/nsFileChannel.h
netwerk/protocol/file/nsFileProtocolHandler.cpp
netwerk/protocol/file/nsFileProtocolHandler.h
netwerk/protocol/file/nsIFileChannel.idl
netwerk/protocol/file/nsIFileProtocolHandler.idl
netwerk/protocol/file/public/Makefile.in
netwerk/protocol/file/public/nsIFileChannel.idl
netwerk/protocol/file/public/nsIFileProtocolHandler.idl
netwerk/protocol/file/src/Makefile.in
netwerk/protocol/file/src/nsFileChannel.cpp
netwerk/protocol/file/src/nsFileChannel.h
netwerk/protocol/file/src/nsFileProtocolHandler.cpp
netwerk/protocol/file/src/nsFileProtocolHandler.h
netwerk/protocol/ftp/Makefile.in
netwerk/protocol/ftp/ftpCore.h
netwerk/protocol/ftp/nsFTPChannel.cpp
netwerk/protocol/ftp/nsFTPChannel.h
netwerk/protocol/ftp/nsFtpConnectionThread.cpp
netwerk/protocol/ftp/nsFtpConnectionThread.h
netwerk/protocol/ftp/nsFtpControlConnection.cpp
netwerk/protocol/ftp/nsFtpControlConnection.h
netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
netwerk/protocol/ftp/nsFtpProtocolHandler.h
netwerk/protocol/ftp/nsIFTPChannel.idl
netwerk/protocol/ftp/public/Makefile.in
netwerk/protocol/ftp/public/ftpCore.h
netwerk/protocol/ftp/public/nsIFTPChannel.idl
netwerk/protocol/ftp/src/Makefile.in
netwerk/protocol/ftp/src/nsFTPChannel.cpp
netwerk/protocol/ftp/src/nsFTPChannel.h
netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp
netwerk/protocol/ftp/src/nsFtpConnectionThread.h
netwerk/protocol/ftp/src/nsFtpControlConnection.cpp
netwerk/protocol/ftp/src/nsFtpControlConnection.h
netwerk/protocol/ftp/src/nsFtpProtocolHandler.cpp
netwerk/protocol/ftp/src/nsFtpProtocolHandler.h
netwerk/protocol/gopher/Makefile.in
netwerk/protocol/gopher/nsGopherChannel.cpp
netwerk/protocol/gopher/nsGopherChannel.h
netwerk/protocol/gopher/nsGopherHandler.cpp
netwerk/protocol/gopher/nsGopherHandler.h
netwerk/protocol/gopher/src/Makefile.in
netwerk/protocol/gopher/src/nsGopherChannel.cpp
netwerk/protocol/gopher/src/nsGopherChannel.h
netwerk/protocol/gopher/src/nsGopherHandler.cpp
netwerk/protocol/gopher/src/nsGopherHandler.h
netwerk/protocol/http/Makefile.in
netwerk/protocol/http/README
netwerk/protocol/http/nsAHttpConnection.h
netwerk/protocol/http/nsAHttpTransaction.h
netwerk/protocol/http/nsHttp.cpp
netwerk/protocol/http/nsHttp.h
netwerk/protocol/http/nsHttpActivityDistributor.cpp
netwerk/protocol/http/nsHttpActivityDistributor.h
netwerk/protocol/http/nsHttpAtomList.h
netwerk/protocol/http/nsHttpAuthCache.cpp
netwerk/protocol/http/nsHttpAuthCache.h
netwerk/protocol/http/nsHttpAuthManager.cpp
netwerk/protocol/http/nsHttpAuthManager.h
netwerk/protocol/http/nsHttpBasicAuth.cpp
netwerk/protocol/http/nsHttpBasicAuth.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpChunkedDecoder.cpp
netwerk/protocol/http/nsHttpChunkedDecoder.h
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnection.h
netwerk/protocol/http/nsHttpConnectionInfo.cpp
netwerk/protocol/http/nsHttpConnectionInfo.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
netwerk/protocol/http/nsHttpDigestAuth.cpp
netwerk/protocol/http/nsHttpDigestAuth.h
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
netwerk/protocol/http/nsHttpHeaderArray.cpp
netwerk/protocol/http/nsHttpHeaderArray.h
netwerk/protocol/http/nsHttpNTLMAuth.cpp
netwerk/protocol/http/nsHttpNTLMAuth.h
netwerk/protocol/http/nsHttpPipeline.cpp
netwerk/protocol/http/nsHttpPipeline.h
netwerk/protocol/http/nsHttpRequestHead.cpp
netwerk/protocol/http/nsHttpRequestHead.h
netwerk/protocol/http/nsHttpResponseHead.cpp
netwerk/protocol/http/nsHttpResponseHead.h
netwerk/protocol/http/nsHttpTransaction.cpp
netwerk/protocol/http/nsHttpTransaction.h
netwerk/protocol/http/nsIHttpActivityObserver.idl
netwerk/protocol/http/nsIHttpAuthManager.idl
netwerk/protocol/http/nsIHttpAuthenticator.idl
netwerk/protocol/http/nsIHttpChannel.idl
netwerk/protocol/http/nsIHttpChannelInternal.idl
netwerk/protocol/http/nsIHttpEventSink.idl
netwerk/protocol/http/nsIHttpHeaderVisitor.idl
netwerk/protocol/http/nsIHttpProtocolHandler.idl
netwerk/protocol/http/public/Makefile.in
netwerk/protocol/http/public/nsIHttpActivityObserver.idl
netwerk/protocol/http/public/nsIHttpAuthManager.idl
netwerk/protocol/http/public/nsIHttpAuthenticator.idl
netwerk/protocol/http/public/nsIHttpChannel.idl
netwerk/protocol/http/public/nsIHttpChannelInternal.idl
netwerk/protocol/http/public/nsIHttpEventSink.idl
netwerk/protocol/http/public/nsIHttpHeaderVisitor.idl
netwerk/protocol/http/public/nsIHttpProtocolHandler.idl
netwerk/protocol/http/src/Makefile.in
netwerk/protocol/http/src/README
netwerk/protocol/http/src/nsAHttpConnection.h
netwerk/protocol/http/src/nsAHttpTransaction.h
netwerk/protocol/http/src/nsHttp.cpp
netwerk/protocol/http/src/nsHttp.h
netwerk/protocol/http/src/nsHttpActivityDistributor.cpp
netwerk/protocol/http/src/nsHttpActivityDistributor.h
netwerk/protocol/http/src/nsHttpAtomList.h
netwerk/protocol/http/src/nsHttpAuthCache.cpp
netwerk/protocol/http/src/nsHttpAuthCache.h
netwerk/protocol/http/src/nsHttpAuthManager.cpp
netwerk/protocol/http/src/nsHttpAuthManager.h
netwerk/protocol/http/src/nsHttpBasicAuth.cpp
netwerk/protocol/http/src/nsHttpBasicAuth.h
netwerk/protocol/http/src/nsHttpChannel.cpp
netwerk/protocol/http/src/nsHttpChannel.h
netwerk/protocol/http/src/nsHttpChunkedDecoder.cpp
netwerk/protocol/http/src/nsHttpChunkedDecoder.h
netwerk/protocol/http/src/nsHttpConnection.cpp
netwerk/protocol/http/src/nsHttpConnection.h
netwerk/protocol/http/src/nsHttpConnectionInfo.cpp
netwerk/protocol/http/src/nsHttpConnectionInfo.h
netwerk/protocol/http/src/nsHttpConnectionMgr.cpp
netwerk/protocol/http/src/nsHttpConnectionMgr.h
netwerk/protocol/http/src/nsHttpDigestAuth.cpp
netwerk/protocol/http/src/nsHttpDigestAuth.h
netwerk/protocol/http/src/nsHttpHandler.cpp
netwerk/protocol/http/src/nsHttpHandler.h
netwerk/protocol/http/src/nsHttpHeaderArray.cpp
netwerk/protocol/http/src/nsHttpHeaderArray.h
netwerk/protocol/http/src/nsHttpNTLMAuth.cpp
netwerk/protocol/http/src/nsHttpNTLMAuth.h
netwerk/protocol/http/src/nsHttpPipeline.cpp
netwerk/protocol/http/src/nsHttpPipeline.h
netwerk/protocol/http/src/nsHttpRequestHead.cpp
netwerk/protocol/http/src/nsHttpRequestHead.h
netwerk/protocol/http/src/nsHttpResponseHead.cpp
netwerk/protocol/http/src/nsHttpResponseHead.h
netwerk/protocol/http/src/nsHttpTransaction.cpp
netwerk/protocol/http/src/nsHttpTransaction.h
netwerk/protocol/res/Makefile.in
netwerk/protocol/res/nsIResProtocolHandler.idl
netwerk/protocol/res/nsResProtocolHandler.cpp
netwerk/protocol/res/nsResProtocolHandler.h
netwerk/protocol/res/public/Makefile.in
netwerk/protocol/res/public/nsIResProtocolHandler.idl
netwerk/protocol/res/src/Makefile.in
netwerk/protocol/res/src/nsResProtocolHandler.cpp
netwerk/protocol/res/src/nsResProtocolHandler.h
netwerk/protocol/viewsource/Makefile.in
netwerk/protocol/viewsource/nsIViewSourceChannel.idl
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
netwerk/protocol/viewsource/nsViewSourceChannel.h
netwerk/protocol/viewsource/nsViewSourceHandler.cpp
netwerk/protocol/viewsource/nsViewSourceHandler.h
netwerk/protocol/viewsource/public/Makefile.in
netwerk/protocol/viewsource/public/nsIViewSourceChannel.idl
netwerk/protocol/viewsource/src/Makefile.in
netwerk/protocol/viewsource/src/nsViewSourceChannel.cpp
netwerk/protocol/viewsource/src/nsViewSourceChannel.h
netwerk/protocol/viewsource/src/nsViewSourceHandler.cpp
netwerk/protocol/viewsource/src/nsViewSourceHandler.h
netwerk/socket/Makefile.in
netwerk/socket/base/Makefile.in
netwerk/socket/base/nsISOCKSSocketInfo.idl
netwerk/socket/base/nsISSLSocketControl.idl
netwerk/socket/base/nsISocketProvider.idl
netwerk/socket/base/nsISocketProviderService.idl
netwerk/socket/base/nsITransportSecurityInfo.idl
netwerk/socket/base/nsSOCKS4SocketProvider.h
netwerk/socket/base/nsSOCKSIOLayer.cpp
netwerk/socket/base/nsSOCKSIOLayer.h
netwerk/socket/base/nsSOCKSSocketProvider.cpp
netwerk/socket/base/nsSOCKSSocketProvider.h
netwerk/socket/base/nsSocketProviderService.cpp
netwerk/socket/base/nsSocketProviderService.h
netwerk/socket/base/nsUDPSocketProvider.cpp
netwerk/socket/base/nsUDPSocketProvider.h
netwerk/socket/nsISOCKSSocketInfo.idl
netwerk/socket/nsISSLSocketControl.idl
netwerk/socket/nsISocketProvider.idl
netwerk/socket/nsISocketProviderService.idl
netwerk/socket/nsITransportSecurityInfo.idl
netwerk/socket/nsSOCKS4SocketProvider.h
netwerk/socket/nsSOCKSIOLayer.cpp
netwerk/socket/nsSOCKSIOLayer.h
netwerk/socket/nsSOCKSSocketProvider.cpp
netwerk/socket/nsSOCKSSocketProvider.h
netwerk/socket/nsSocketProviderService.cpp
netwerk/socket/nsSocketProviderService.h
netwerk/socket/nsUDPSocketProvider.cpp
netwerk/socket/nsUDPSocketProvider.h
netwerk/test/Makefile.in
netwerk/wifi/Makefile.in
netwerk/wifi/nsIWifiAccessPoint.idl
netwerk/wifi/nsIWifiListener.idl
netwerk/wifi/nsIWifiMonitor.idl
netwerk/wifi/nsWifiAccessPoint.cpp
netwerk/wifi/nsWifiAccessPoint.h
netwerk/wifi/nsWifiMonitor.cpp
netwerk/wifi/nsWifiMonitor.h
netwerk/wifi/nsWifiScannerMac.cpp
netwerk/wifi/nsWifiScannerSolaris.cpp
netwerk/wifi/nsWifiScannerUnix.cpp
netwerk/wifi/nsWifiScannerWin.cpp
netwerk/wifi/osx_corewlan.mm
netwerk/wifi/osx_wifi.h
netwerk/wifi/public/Makefile.in
netwerk/wifi/public/nsIWifiAccessPoint.idl
netwerk/wifi/public/nsIWifiListener.idl
netwerk/wifi/public/nsIWifiMonitor.idl
netwerk/wifi/src/Makefile.in
netwerk/wifi/src/nsWifiAccessPoint.cpp
netwerk/wifi/src/nsWifiAccessPoint.h
netwerk/wifi/src/nsWifiMonitor.cpp
netwerk/wifi/src/nsWifiMonitor.h
netwerk/wifi/src/nsWifiScannerMac.cpp
netwerk/wifi/src/nsWifiScannerSolaris.cpp
netwerk/wifi/src/nsWifiScannerUnix.cpp
netwerk/wifi/src/nsWifiScannerWin.cpp
netwerk/wifi/src/osx_corewlan.mm
netwerk/wifi/src/osx_wifi.h
netwerk/wifi/src/wlanapi.h
netwerk/wifi/wlanapi.h
toolkit/toolkit-makefiles.sh
--- a/netwerk/build/Makefile.in
+++ b/netwerk/build/Makefile.in
@@ -30,47 +30,46 @@
 # 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 *****
 
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
+DEPTH     = ../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MODULE		= necko
-LIBRARY_NAME	= necko
+MODULE         = necko
+LIBRARY_NAME   = necko
 EXPORT_LIBRARY = 1
-IS_COMPONENT	= 1
-MODULE_NAME	= necko
-GRE_MODULE	= 1
+IS_COMPONENT   = 1
+MODULE_NAME    = necko
+GRE_MODULE     = 1
 LIBXUL_LIBRARY = 1
 
-
-CPPSRCS		= nsNetModule.cpp
-EXPORTS		= nsNetCID.h
+CPPSRCS = nsNetModule.cpp
+EXPORTS = nsNetCID.h
 
 SHARED_LIBRARY_LIBS = \
-		../base/src/$(LIB_PREFIX)neckobase_s.$(LIB_SUFFIX) \
-		../dns/src/$(LIB_PREFIX)neckodns_s.$(LIB_SUFFIX) \
-		../socket/base/$(LIB_PREFIX)neckosocket_s.$(LIB_SUFFIX) \
-		../streamconv/src/$(LIB_PREFIX)nkconv_s.$(LIB_SUFFIX) \
-		../streamconv/converters/$(LIB_PREFIX)nkcnvts_s.$(LIB_SUFFIX) \
-		../mime/src/$(LIB_PREFIX)nkmime_s.$(LIB_SUFFIX) \
-		../cache/src/$(LIB_PREFIX)nkcache_s.$(LIB_SUFFIX) \
-		../protocol/about/src/$(LIB_PREFIX)nkabout_s.$(LIB_SUFFIX) \
-		$(foreach d,$(filter-out about,$(NECKO_PROTOCOLS)), \
-			../protocol/$(d)/src/$(LIB_PREFIX)nk$(d)_s.$(LIB_SUFFIX)) \
-		$(NULL)
+  ../base/src/$(LIB_PREFIX)neckobase_s.$(LIB_SUFFIX) \
+  ../dns/$(LIB_PREFIX)neckodns_s.$(LIB_SUFFIX) \
+  ../socket/$(LIB_PREFIX)neckosocket_s.$(LIB_SUFFIX) \
+  ../streamconv/src/$(LIB_PREFIX)nkconv_s.$(LIB_SUFFIX) \
+  ../streamconv/converters/$(LIB_PREFIX)nkcnvts_s.$(LIB_SUFFIX) \
+  ../mime/$(LIB_PREFIX)nkmime_s.$(LIB_SUFFIX) \
+  ../cache/$(LIB_PREFIX)nkcache_s.$(LIB_SUFFIX) \
+  ../protocol/about/$(LIB_PREFIX)nkabout_s.$(LIB_SUFFIX) \
+  $(foreach d,$(filter-out about,$(NECKO_PROTOCOLS)), \
+    ../protocol/$(d)/$(LIB_PREFIX)nk$(d)_s.$(LIB_SUFFIX)) \
+  $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
     SHARED_LIBRARY_LIBS += \
         ../system/win32/$(LIB_PREFIX)neckosystem_s.$(LIB_SUFFIX)
 endif
 
 ifeq ($(OS_ARCH),WINCE)
     SHARED_LIBRARY_LIBS += \
@@ -82,28 +81,28 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
         ../system/mac/$(LIB_PREFIX)neckosystem_s.$(LIB_SUFFIX)
 endif
 
 ifdef MOZ_ENABLE_LIBCONIC
     SHARED_LIBRARY_LIBS += \
         ../system/maemo/$(LIB_PREFIX)neckosystem_s.$(LIB_SUFFIX)
 endif
 
-LOCAL_INCLUDES	= \
-		-I$(srcdir)/../base/src \
-		-I$(srcdir)/../dns/src \
-		-I$(srcdir)/../socket/base \
-		-I$(srcdir)/../streamconv/src \
-		-I$(srcdir)/../streamconv/converters \
-		-I$(srcdir)/../mime/src \
-		-I$(srcdir)/../cache/src \
-		-I$(srcdir)/../protocol/about/src \
-		$(foreach d,$(filter-out about,$(NECKO_PROTOCOLS)), \
-			-I$(srcdir)/../protocol/$(d)/src) \
-		$(NULL)
+LOCAL_INCLUDES = \
+  -I$(srcdir)/../base/src \
+  -I$(srcdir)/../dns \
+  -I$(srcdir)/../socket \
+  -I$(srcdir)/../streamconv/src \
+  -I$(srcdir)/../streamconv/converters \
+  -I$(srcdir)/../mime \
+  -I$(srcdir)/../cache \
+  -I$(srcdir)/../protocol/about \
+  $(foreach d,$(filter-out about,$(NECKO_PROTOCOLS)), \
+    -I$(srcdir)/../protocol/$(d)) \
+  $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
     LOCAL_INCLUDES += -I$(srcdir)/../system/win32
 endif
 
 ifeq ($(OS_ARCH),WINCE)
     LOCAL_INCLUDES += -I$(srcdir)/../system/wince
 endif
@@ -113,67 +112,67 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 endif
 
 ifdef MOZ_ENABLE_LIBCONIC
     LOCAL_INCLUDES += -I$(srcdir)/../system/maemo
 endif
 
 ifdef NECKO_COOKIES
 SHARED_LIBRARY_LIBS += \
-		../cookie/src/$(LIB_PREFIX)neckocookie_s.$(LIB_SUFFIX) \
-                $(NULL)
-LOCAL_INCLUDES	+= -I$(srcdir)/../cookie/src
+  ../cookie/$(LIB_PREFIX)neckocookie_s.$(LIB_SUFFIX) \
+  $(NULL)
+LOCAL_INCLUDES += -I$(srcdir)/../cookie
 endif
 
 ifdef NECKO_WIFI
 SHARED_LIBRARY_LIBS += \
-		../wifi/src/$(LIB_PREFIX)neckowifi_s.$(LIB_SUFFIX) \
-                $(NULL)
-LOCAL_INCLUDES	+= -I$(srcdir)/../wifi/src
+  ../wifi/$(LIB_PREFIX)neckowifi_s.$(LIB_SUFFIX) \
+  $(NULL)
+LOCAL_INCLUDES += -I$(srcdir)/../wifi
 
 ifeq ($(OS_ARCH),SunOS)
 OS_LIBS += $(GLIB_LIBS)
 endif
 
 endif
 
 
 ifdef MOZ_STORAGE
 DEFINES += -DNECKO_OFFLINE_CACHE
 endif
 
 EXTRA_DSO_LDOPTS = \
-		$(LIBS_DIR) \
-		$(EXTRA_DSO_LIBS) \
-		$(MOZ_UNICHARUTIL_LIBS) \
-		$(MOZ_COMPONENT_LIBS) \
-		$(ZLIB_LIBS) \
-		$(NULL)
+  $(LIBS_DIR) \
+  $(EXTRA_DSO_LIBS) \
+  $(MOZ_UNICHARUTIL_LIBS) \
+  $(MOZ_COMPONENT_LIBS) \
+  $(ZLIB_LIBS) \
+  $(NULL)
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 EXTRA_DSO_LDOPTS += \
-		-framework SystemConfiguration \
-		$(TK_LIBS) \
-	$(NULL)
+  -framework SystemConfiguration \
+  $(TK_LIBS) \
+  $(NULL)
 endif
 
 ifdef MOZ_ENABLE_LIBCONIC
 EXTRA_DSO_LDOPTS += \
-		$(LIBCONIC_LIBS) \
-		$(MOZ_DBUS_LIBS) \
-		$(NULL)
+  $(LIBCONIC_LIBS) \
+  $(MOZ_DBUS_LIBS) \
+  $(NULL)
 endif
 
 ifeq ($(OS_ARCH),AIX)
 EXTRA_DSO_LDOPTS += -lodm -lcfg
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 ifeq ($(OS_ARCH),WINNT)
-OS_LIBS	+= $(call EXPAND_LIBNAME,ole32 shell32)
+OS_LIBS += $(call EXPAND_LIBNAME,ole32 shell32)
 endif
 
 ifeq ($(OS_ARCH),WINCE)
-OS_LIBS	+= $(call EXPAND_LIBNAME,cellcore ws2)
+OS_LIBS += $(call EXPAND_LIBNAME,cellcore ws2)
 endif
 
 DEFINES += -DIMPL_NS_NET
--- a/netwerk/cache/Makefile.in
+++ b/netwerk/cache/Makefile.in
@@ -30,21 +30,73 @@
 # 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 *****
 
-DEPTH		= ../..
-topsrcdir	= @top_srcdir@
-srcdir		= @srcdir@
-VPATH		= @srcdir@
+DEPTH     = ../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MODULE	= nkcache
-DIRS	= public src
+MODULE         = nkcache
+LIBRARY_NAME   = nkcache_s
+LIBXUL_LIBRARY = 1
+XPIDL_MODULE   = necko_cache
+GRE_MODULE     = 1
+
+FORCE_STATIC_LIB = 1
+
+XPIDLSRCS = \
+  nsICache.idl \
+  nsICacheEntryDescriptor.idl \
+  nsICacheListener.idl \
+  nsICacheService.idl \
+  nsICacheSession.idl \
+  nsICacheVisitor.idl \
+  $(NULL)
+
+EXPORTS = \
+  nsCacheService.h \
+  $(NULL)
+
+CPPSRCS = \
+  nsCache.cpp \
+  nsCacheEntry.cpp \
+  nsCacheEntryDescriptor.cpp \
+  nsCacheMetaData.cpp \
+  nsCacheService.cpp \
+  nsCacheSession.cpp \
+  nsMemoryCacheDevice.cpp \
+  $(NULL)
+
+ifdef NECKO_DISK_CACHE
+CPPSRCS += \
+  nsDiskCacheBinding.cpp \
+  nsDiskCacheBlockFile.cpp \
+  nsDiskCacheDevice.cpp \
+  nsDiskCacheEntry.cpp \
+  nsDiskCacheMap.cpp \
+  nsDiskCacheStreams.cpp \
+  nsDeleteDir.cpp \
+  $(NULL)
+endif
+
+ifdef MOZ_STORAGE
+CPPSRCS += \
+  nsDiskCacheDeviceSQL.cpp \
+  $(NULL)
+
+DEFINES += -DNECKO_OFFLINE_CACHE
+endif
+
+LOCAL_INCLUDES = \
+  -I$(srcdir)/../base/src \
+  $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -DIMPL_NS_NET
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCache.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCache.cpp, released
+ * March 18, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan  <gordon@netscape.com>
+ *   Patrick C. Beard <beard@netscape.com>
+ *   Darin Fisher     <darin@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 "nsCache.h"
+#include "nsReadableUtils.h"
+#include "nsDependentSubstring.h"
+#include "nsString.h"
+
+
+/**
+ * Cache Service Utility Functions
+ */
+
+#if defined(PR_LOGGING)
+PRLogModuleInfo * gCacheLog = nsnull;
+
+
+void
+CacheLogInit()
+{
+    if (gCacheLog) return;
+    gCacheLog = PR_NewLogModule("cache");
+    NS_ASSERTION(gCacheLog, "\nfailed to allocate cache log.\n");
+}
+
+
+void
+CacheLogPrintPath(PRLogModuleLevel level, const char * format, nsIFile * item)
+{
+    nsCAutoString path;
+    nsresult rv = item->GetNativePath(path);
+    if (NS_SUCCEEDED(rv)) {
+        PR_LOG(gCacheLog, level, (format, path.get()));
+    } else {
+        PR_LOG(gCacheLog, level, ("GetNativePath failed: %x", rv));
+    }
+}
+
+#endif
+
+
+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;
+}
+
+
+PRTime
+PRTimeFromSeconds(PRUint32 seconds)
+{
+  PRInt64 microSecondsPerSecond, intermediateResult;
+  PRTime  prTime;
+
+  LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
+  LL_UI2L(intermediateResult, seconds);
+  LL_MUL(prTime, intermediateResult, microSecondsPerSecond);
+  return prTime;
+}
+
+
+nsresult
+ClientIDFromCacheKey(const nsACString&  key, char ** result)
+{
+    nsresult  rv = NS_OK;
+    *result = nsnull;
+
+    nsReadingIterator<char> colon;
+    key.BeginReading(colon);
+        
+    nsReadingIterator<char> start;
+    key.BeginReading(start);
+        
+    nsReadingIterator<char> end;
+    key.EndReading(end);
+        
+    if (FindCharInReadable(':', colon, end)) {
+        *result = ToNewCString( Substring(start, colon));
+        if (!*result) rv = NS_ERROR_OUT_OF_MEMORY;
+    } else {
+        NS_ASSERTION(PR_FALSE, "FindCharInRead failed to find ':'");
+        rv = NS_ERROR_UNEXPECTED;
+    }
+    return rv;
+}
+
+
+nsresult
+ClientKeyFromCacheKey(const nsCString& key, nsACString &result)
+{
+    nsresult  rv = NS_OK;
+
+    nsReadingIterator<char> start;
+    key.BeginReading(start);
+        
+    nsReadingIterator<char> end;
+    key.EndReading(end);
+        
+    if (FindCharInReadable(':', start, end)) {
+        ++start;  // advance past clientID ':' delimiter
+        result.Assign(Substring(start, end));
+    } else {
+        NS_ASSERTION(PR_FALSE, "FindCharInRead failed to find ':'");
+        rv = NS_ERROR_UNEXPECTED;
+        result.Truncate(0);
+    }
+    return rv;
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCache.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCache.h, released
+ * March 18, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan  <gordon@netscape.com>
+ *   Patrick C. Beard <beard@netscape.com>
+ *   Darin Fisher     <darin@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 ***** */
+
+/**
+ * Cache Service Utility Functions
+ */
+
+#ifndef _nsCache_h_
+#define _nsCache_h_
+
+#include "nsISupports.h"
+#include "nsIFile.h"
+#include "nsAString.h"
+#include "prtime.h"
+#include "nsError.h"
+#include "prlog.h"
+
+// PR_LOG args = "format string", arg, arg, ...
+#if defined(PR_LOGGING)
+extern PRLogModuleInfo * gCacheLog;
+void   CacheLogInit();
+void   CacheLogPrintPath(PRLogModuleLevel level,
+                         const char *     format,
+                         nsIFile *        item);
+#define CACHE_LOG_INIT()        CacheLogInit()
+#define CACHE_LOG_ALWAYS(args)  PR_LOG(gCacheLog, PR_LOG_ALWAYS, args)
+#define CACHE_LOG_ERROR(args)   PR_LOG(gCacheLog, PR_LOG_ERROR, args)
+#define CACHE_LOG_WARNING(args) PR_LOG(gCacheLog, PR_LOG_WARNING, args)
+#define CACHE_LOG_DEBUG(args)   PR_LOG(gCacheLog, PR_LOG_DEBUG, args)
+#define CACHE_LOG_PATH(level, format, item) \
+                                CacheLogPrintPath(level, format, item)
+#else
+#define CACHE_LOG_INIT()        {}
+#define CACHE_LOG_ALWAYS(args)  {}
+#define CACHE_LOG_ERROR(args)   {}
+#define CACHE_LOG_WARNING(args) {}
+#define CACHE_LOG_DEBUG(args)   {}
+#define CACHE_LOG_PATH(level, format, item)  {}
+#endif
+
+
+extern PRUint32  SecondsFromPRTime(PRTime prTime);
+extern PRTime    PRTimeFromSeconds(PRUint32 seconds);
+
+
+extern nsresult  ClientIDFromCacheKey(const nsACString&  key, char ** result);
+extern nsresult  ClientKeyFromCacheKey(const nsCString& key, nsACString &result);
+
+
+#endif // _nsCache_h
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheDevice.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheDevice.h, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan, 22-February-2001
+ *
+ * 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 ***** */
+
+#ifndef _nsCacheDevice_h_
+#define _nsCacheDevice_h_
+
+#include "nspr.h"
+#include "nsError.h"
+#include "nsICache.h"
+
+class nsIFile;
+class nsCString;
+class nsCacheEntry;
+class nsICacheVisitor;
+class nsIInputStream;
+class nsIOutputStream;
+
+/******************************************************************************
+* nsCacheDevice
+*******************************************************************************/
+class nsCacheDevice {
+public:
+    nsCacheDevice() { MOZ_COUNT_CTOR(nsCacheDevice); }
+    virtual ~nsCacheDevice() { MOZ_COUNT_DTOR(nsCacheDevice); }
+
+    virtual nsresult Init() = 0;
+    virtual nsresult Shutdown() = 0;
+
+    virtual const char *   GetDeviceID(void) = 0;
+    virtual nsCacheEntry * FindEntry( nsCString * key, PRBool *collision ) = 0;
+
+    virtual nsresult DeactivateEntry( nsCacheEntry * entry ) = 0;
+    virtual nsresult BindEntry( nsCacheEntry * entry ) = 0;
+    virtual void     DoomEntry( nsCacheEntry * entry ) = 0;
+
+    virtual nsresult OpenInputStreamForEntry(nsCacheEntry *     entry,
+                                             nsCacheAccessMode  mode,
+                                             PRUint32           offset,
+                                             nsIInputStream **  result) = 0;
+
+    virtual nsresult OpenOutputStreamForEntry(nsCacheEntry *     entry,
+                                              nsCacheAccessMode  mode,
+                                              PRUint32           offset,
+                                              nsIOutputStream ** result) = 0;
+
+    virtual nsresult GetFileForEntry( nsCacheEntry *    entry,
+                                      nsIFile **        result ) = 0;
+
+    virtual nsresult OnDataSizeChange( nsCacheEntry * entry, PRInt32 deltaSize ) = 0;
+
+    virtual nsresult Visit(nsICacheVisitor * visitor) = 0;
+
+    /**
+     * Device must evict entries associated with clientID.  If clientID == nsnull, all
+     * entries must be evicted.  Active entries must be doomed, rather than evicted.
+     */
+    virtual nsresult EvictEntries(const char * clientID) = 0;
+};
+
+#endif // _nsCacheDevice_h_
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheEntry.cpp
@@ -0,0 +1,536 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheEntry.cpp, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan <gordon@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 "nspr.h"
+#include "nsCacheEntry.h"
+#include "nsCacheEntryDescriptor.h"
+#include "nsCacheMetaData.h"
+#include "nsCacheRequest.h"
+#include "nsThreadUtils.h"
+#include "nsError.h"
+#include "nsICacheService.h"
+#include "nsCache.h"
+#include "nsCacheService.h"
+#include "nsCacheDevice.h"
+#include "nsCRT.h"
+
+
+nsCacheEntry::nsCacheEntry(nsCString *          key,
+                           PRBool               streamBased,
+                           nsCacheStoragePolicy storagePolicy)
+    : mKey(key),
+      mFetchCount(0),
+      mLastFetched(0),
+      mLastModified(0),
+      mExpirationTime(NO_EXPIRATION_TIME),
+      mFlags(0),
+      mDataSize(0),
+      mCacheDevice(nsnull),
+      mData(nsnull)
+{
+    MOZ_COUNT_CTOR(nsCacheEntry);
+    PR_INIT_CLIST(this);
+    PR_INIT_CLIST(&mRequestQ);
+    PR_INIT_CLIST(&mDescriptorQ);
+
+    if (streamBased) MarkStreamBased();
+    SetStoragePolicy(storagePolicy);
+}
+
+
+nsCacheEntry::~nsCacheEntry()
+{
+    MOZ_COUNT_DTOR(nsCacheEntry);
+    delete mKey;
+    
+    if (mData)
+        nsCacheService::ReleaseObject_Locked(mData, mThread);
+}
+
+
+nsresult
+nsCacheEntry::Create( const char *          key,
+                      PRBool                streamBased,
+                      nsCacheStoragePolicy  storagePolicy,
+                      nsCacheDevice *       device,
+                      nsCacheEntry **       result)
+{
+    nsCString* newKey = new nsCString(key);
+    if (!newKey) return NS_ERROR_OUT_OF_MEMORY;
+    
+    nsCacheEntry* entry = new nsCacheEntry(newKey, streamBased, storagePolicy);
+    if (!entry) { delete newKey; return NS_ERROR_OUT_OF_MEMORY; }
+    
+    entry->SetCacheDevice(device);
+    
+    *result = entry;
+    return NS_OK;
+}
+
+
+void
+nsCacheEntry::Fetched()
+{
+    mLastFetched = SecondsFromPRTime(PR_Now());
+    ++mFetchCount;
+    MarkEntryDirty();
+}
+
+
+const char *
+nsCacheEntry::GetDeviceID()
+{
+    if (mCacheDevice)  return mCacheDevice->GetDeviceID();
+    return nsnull;
+}
+
+
+void
+nsCacheEntry::TouchData()
+{
+    mLastModified = SecondsFromPRTime(PR_Now());
+    MarkDataDirty();
+}
+
+
+void
+nsCacheEntry::SetData(nsISupports * data)
+{
+    if (mData) {
+        nsCacheService::ReleaseObject_Locked(mData, mThread);
+        mData = nsnull;
+    }
+
+    if (data) {
+        NS_ADDREF(mData = data);
+        mThread = do_GetCurrentThread();
+    }
+}
+
+
+void
+nsCacheEntry::TouchMetaData()
+{
+    mLastModified = SecondsFromPRTime(PR_Now());
+    MarkMetaDataDirty();
+}
+
+
+/**
+ *  cache entry states
+ *      0 descriptors (new entry)
+ *      0 descriptors (existing, bound entry)
+ *      n descriptors (existing, bound entry) valid
+ *      n descriptors (existing, bound entry) not valid (wait until valid or doomed)
+ */
+
+nsresult
+nsCacheEntry::RequestAccess(nsCacheRequest * request, nsCacheAccessMode *accessGranted)
+{
+    nsresult  rv = NS_OK;
+    
+    if (!IsInitialized()) {
+        // brand new, unbound entry
+        request->mKey = nsnull;  // steal ownership of the key string
+        if (request->IsStreamBased())  MarkStreamBased();
+        MarkInitialized();
+
+        *accessGranted = request->AccessRequested() & nsICache::ACCESS_WRITE;
+        NS_ASSERTION(*accessGranted, "new cache entry for READ-ONLY request");
+        PR_APPEND_LINK(request, &mRequestQ);
+        return rv;
+    }
+    
+    if (IsDoomed()) return NS_ERROR_CACHE_ENTRY_DOOMED;
+
+    if (IsStreamData() != request->IsStreamBased()) {
+        *accessGranted = nsICache::ACCESS_NONE;
+        return request->IsStreamBased() ?
+            NS_ERROR_CACHE_DATA_IS_NOT_STREAM : NS_ERROR_CACHE_DATA_IS_STREAM;
+    }
+
+    if (PR_CLIST_IS_EMPTY(&mDescriptorQ)) {
+        // 1st descriptor for existing bound entry
+        *accessGranted = request->AccessRequested();
+        if (*accessGranted & nsICache::ACCESS_WRITE) {
+            MarkInvalid();
+        } else {
+            MarkValid();
+        }
+    } else {
+        // nth request for existing, bound entry
+        *accessGranted = request->AccessRequested() & ~nsICache::ACCESS_WRITE;
+        if (!IsValid())
+            rv = NS_ERROR_CACHE_WAIT_FOR_VALIDATION;
+    }
+    PR_APPEND_LINK(request,&mRequestQ);
+
+    return rv;
+}
+
+
+nsresult
+nsCacheEntry::CreateDescriptor(nsCacheRequest *           request,
+                               nsCacheAccessMode          accessGranted,
+                               nsICacheEntryDescriptor ** result)
+{
+    NS_ENSURE_ARG_POINTER(request && result);
+
+    nsCacheEntryDescriptor * descriptor =
+        new nsCacheEntryDescriptor(this, accessGranted);
+
+    // XXX check request is on q
+    PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success
+
+    if (descriptor == nsnull)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    PR_APPEND_LINK(descriptor, &mDescriptorQ);
+
+    NS_ADDREF(*result = descriptor);
+    return NS_OK;
+}
+
+
+PRBool
+nsCacheEntry::RemoveRequest(nsCacheRequest * request)
+{
+    // XXX if debug: verify this request belongs to this entry
+    PR_REMOVE_AND_INIT_LINK(request);
+
+    // return true if this entry should stay active
+    return !((PR_CLIST_IS_EMPTY(&mRequestQ)) &&
+             (PR_CLIST_IS_EMPTY(&mDescriptorQ)));
+}
+
+
+PRBool
+nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor)
+{
+    NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!");
+    PR_REMOVE_AND_INIT_LINK(descriptor);
+    descriptor->ClearCacheEntry();
+
+    if (!PR_CLIST_IS_EMPTY(&mDescriptorQ))
+        return PR_TRUE;  // stay active if we still have open descriptors
+
+    if (PR_CLIST_IS_EMPTY(&mRequestQ))
+        return PR_FALSE; // no descriptors or requests, we can deactivate
+
+    return PR_TRUE;     // find next best request to give a descriptor to
+}
+
+
+void
+nsCacheEntry::DetachDescriptors(void)
+{
+    nsCacheEntryDescriptor * descriptor =
+        (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ);
+
+    while (descriptor != &mDescriptorQ) {
+        nsCacheEntryDescriptor * nextDescriptor =
+            (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor);
+        
+        descriptor->ClearCacheEntry();
+        PR_REMOVE_AND_INIT_LINK(descriptor);
+        descriptor = nextDescriptor;
+    }
+}
+
+
+/******************************************************************************
+ * nsCacheEntryInfo - for implementing about:cache
+ *****************************************************************************/
+
+NS_IMPL_ISUPPORTS1(nsCacheEntryInfo, nsICacheEntryInfo)
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetClientID(char ** clientID)
+{
+    NS_ENSURE_ARG_POINTER(clientID);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    return ClientIDFromCacheKey(*mCacheEntry->Key(), clientID);
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetDeviceID(char ** deviceID)
+{
+    NS_ENSURE_ARG_POINTER(deviceID);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *deviceID = NS_strdup(mCacheEntry->GetDeviceID());
+    return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetKey(nsACString &key)
+{
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    return ClientKeyFromCacheKey(*mCacheEntry->Key(), key);
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetFetchCount(PRInt32 * fetchCount)
+{
+    NS_ENSURE_ARG_POINTER(fetchCount);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *fetchCount = mCacheEntry->FetchCount();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetLastFetched(PRUint32 * lastFetched)
+{
+    NS_ENSURE_ARG_POINTER(lastFetched);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *lastFetched = mCacheEntry->LastFetched();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetLastModified(PRUint32 * lastModified)
+{
+    NS_ENSURE_ARG_POINTER(lastModified);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *lastModified = mCacheEntry->LastModified();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetExpirationTime(PRUint32 * expirationTime)
+{
+    NS_ENSURE_ARG_POINTER(expirationTime);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *expirationTime = mCacheEntry->ExpirationTime();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::GetDataSize(PRUint32 * dataSize)
+{
+    NS_ENSURE_ARG_POINTER(dataSize);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *dataSize = mCacheEntry->DataSize();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryInfo::IsStreamBased(PRBool * result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+    
+    *result = mCacheEntry->IsStreamData();
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  nsCacheEntryHashTable
+ *****************************************************************************/
+
+PLDHashTableOps
+nsCacheEntryHashTable::ops =
+{
+    PL_DHashAllocTable,
+    PL_DHashFreeTable,
+    HashKey,
+    MatchEntry,
+    MoveEntry,
+    ClearEntry,
+    PL_DHashFinalizeStub
+};
+
+
+nsCacheEntryHashTable::nsCacheEntryHashTable()
+    : initialized(PR_FALSE)
+{
+    MOZ_COUNT_CTOR(nsCacheEntryHashTable);
+}
+
+
+nsCacheEntryHashTable::~nsCacheEntryHashTable()
+{
+    MOZ_COUNT_DTOR(nsCacheEntryHashTable);
+    if (initialized)
+        Shutdown();
+}
+
+
+nsresult
+nsCacheEntryHashTable::Init()
+{
+    nsresult rv = NS_OK;
+    initialized = PL_DHashTableInit(&table, &ops, nsnull,
+                                           sizeof(nsCacheEntryHashTableEntry), 512);
+
+    if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY;
+    
+    return rv;
+}
+
+void
+nsCacheEntryHashTable::Shutdown()
+{
+    if (initialized) {
+        PL_DHashTableFinish(&table);
+        initialized = PR_FALSE;
+    }
+}
+
+
+nsCacheEntry *
+nsCacheEntryHashTable::GetEntry( const nsCString * key)
+{
+    PLDHashEntryHdr *hashEntry;
+    nsCacheEntry    *result = nsnull;
+
+    NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
+    if (!initialized)  return nsnull;
+    
+    hashEntry = PL_DHashTableOperate(&table, key, PL_DHASH_LOOKUP);
+    if (PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
+        result = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
+    }
+    return result;
+}
+
+
+nsresult
+nsCacheEntryHashTable::AddEntry( nsCacheEntry *cacheEntry)
+{
+    PLDHashEntryHdr    *hashEntry;
+
+    NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
+    if (!initialized)  return NS_ERROR_NOT_INITIALIZED;
+    if (!cacheEntry)   return NS_ERROR_NULL_POINTER;
+
+    hashEntry = PL_DHashTableOperate(&table, cacheEntry->mKey, PL_DHASH_ADD);
+#ifndef DEBUG_dougt
+    NS_ASSERTION(((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry == 0,
+                 "### nsCacheEntryHashTable::AddEntry - entry already used");
+#endif
+    ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = cacheEntry;
+
+    return NS_OK;
+}
+
+
+void
+nsCacheEntryHashTable::RemoveEntry( nsCacheEntry *cacheEntry)
+{
+    NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
+    NS_ASSERTION(cacheEntry, "### cacheEntry == nsnull");
+
+    if (!initialized)  return; // NS_ERROR_NOT_INITIALIZED
+
+#if DEBUG
+    // XXX debug code to make sure we have the entry we're trying to remove
+    nsCacheEntry *check = GetEntry(cacheEntry->mKey);
+    NS_ASSERTION(check == cacheEntry, "### Attempting to remove unknown cache entry!!!");
+#endif
+    (void) PL_DHashTableOperate(&table, cacheEntry->mKey, PL_DHASH_REMOVE);
+}
+
+
+void
+nsCacheEntryHashTable::VisitEntries( PLDHashEnumerator etor, void *arg)
+{
+    NS_ASSERTION(initialized, "nsCacheEntryHashTable not initialized");
+    if (!initialized)  return; // NS_ERROR_NOT_INITIALIZED
+    PL_DHashTableEnumerate(&table, etor, arg);
+}
+
+
+/**
+ *  hash table operation callback functions
+ */
+
+PLDHashNumber
+nsCacheEntryHashTable::HashKey( PLDHashTable *table, const void *key)
+{
+    return PL_DHashStringKey(table,((nsCString *)key)->get());
+}
+
+PRBool
+nsCacheEntryHashTable::MatchEntry(PLDHashTable *       /* table */,
+                                  const PLDHashEntryHdr * hashEntry,
+                                  const void *            key)
+{
+    NS_ASSERTION(key !=  nsnull, "### nsCacheEntryHashTable::MatchEntry : null key");
+    nsCacheEntry *cacheEntry = ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry;
+
+    return cacheEntry->mKey->Equals(*(nsCString *)key);
+}
+
+
+void
+nsCacheEntryHashTable::MoveEntry(PLDHashTable * /* table */,
+                                 const PLDHashEntryHdr *from,
+                                 PLDHashEntryHdr       *to)
+{
+    ((nsCacheEntryHashTableEntry *)to)->cacheEntry =
+        ((nsCacheEntryHashTableEntry *)from)->cacheEntry;
+}
+
+
+void
+nsCacheEntryHashTable::ClearEntry(PLDHashTable * /* table */,
+                                  PLDHashEntryHdr * hashEntry)
+{
+    ((nsCacheEntryHashTableEntry *)hashEntry)->cacheEntry = 0;
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheEntry.h
@@ -0,0 +1,333 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheEntry.h, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan <gordon@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 ***** */
+
+#ifndef _nsCacheEntry_h_
+#define _nsCacheEntry_h_
+
+#include "nsICache.h"
+#include "nsICacheEntryDescriptor.h"
+#include "nsIThread.h"
+#include "nsCacheMetaData.h"
+
+#include "nspr.h"
+#include "pldhash.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsAString.h"
+
+class nsCacheDevice;
+class nsCacheMetaData;
+class nsCacheRequest;
+class nsCacheEntryDescriptor;
+
+#define NO_EXPIRATION_TIME  0xFFFFFFFF
+
+/******************************************************************************
+* nsCacheEntry
+*******************************************************************************/
+class nsCacheEntry : public PRCList
+{
+public:
+
+    nsCacheEntry(nsCString *          key,
+                 PRBool               streamBased,
+                 nsCacheStoragePolicy storagePolicy);
+    ~nsCacheEntry();
+
+
+    static nsresult  Create( const char *          key,
+                             PRBool                streamBased,
+                             nsCacheStoragePolicy  storagePolicy,
+                             nsCacheDevice *       device,
+                             nsCacheEntry **       result);
+                                      
+    nsCString *  Key()  { return mKey; }
+
+    PRInt32  FetchCount()                              { return mFetchCount; }
+    void     SetFetchCount( PRInt32   count)           { mFetchCount = count; }
+    void     Fetched();
+
+    PRUint32 LastFetched()                             { return mLastFetched; }
+    void     SetLastFetched( PRUint32  lastFetched)    { mLastFetched = lastFetched; }
+
+    PRUint32 LastModified()                            { return mLastModified; }
+    void     SetLastModified( PRUint32 lastModified)   { mLastModified = lastModified; }
+
+    PRUint32 ExpirationTime()                     { return mExpirationTime; }
+    void     SetExpirationTime( PRUint32 expires) { mExpirationTime = expires; }
+
+    PRUint32 Size()                               { return mDataSize + mMetaData.Size(); }
+
+    nsCacheDevice * CacheDevice()                            { return mCacheDevice; }
+    void            SetCacheDevice( nsCacheDevice * device)  { mCacheDevice = device; }
+    const char *    GetDeviceID();
+
+    /**
+     * Data accessors
+     */
+    nsISupports *Data()                           { return mData; }
+    void         SetData( nsISupports * data);
+
+    PRUint32 DataSize()                           { return mDataSize; }
+    void     SetDataSize( PRUint32  size)         { mDataSize = size; }
+
+    void     TouchData();
+    
+    /**
+     * Meta data accessors
+     */
+    const char * GetMetaDataElement( const char *  key) { return mMetaData.GetElement(key); }
+    nsresult     SetMetaDataElement( const char *  key,
+                                     const char *  value) { return mMetaData.SetElement(key, value); }
+    nsresult VisitMetaDataElements( nsICacheMetaDataVisitor * visitor) { return mMetaData.VisitElements(visitor); }
+    nsresult FlattenMetaData(char * buffer, PRUint32 bufSize) { return mMetaData.FlattenMetaData(buffer, bufSize); }
+    nsresult UnflattenMetaData(const char * buffer, PRUint32 bufSize) { return mMetaData.UnflattenMetaData(buffer, bufSize); }
+    PRUint32 MetaDataSize() { return mMetaData.Size(); }  
+
+    void     TouchMetaData();
+
+
+    /**
+     * Security Info accessors
+     */
+    nsISupports* SecurityInfo() { return mSecurityInfo; }
+    void     SetSecurityInfo( nsISupports *  info) { mSecurityInfo = info; }
+
+
+    // XXX enumerate MetaData method
+
+
+    enum CacheEntryFlags {
+        eStoragePolicyMask   = 0x000000FF,
+        eDoomedMask          = 0x00000100,
+        eEntryDirtyMask      = 0x00000200,
+        eDataDirtyMask       = 0x00000400,
+        eMetaDataDirtyMask   = 0x00000800,
+        eStreamDataMask      = 0x00001000,
+        eActiveMask          = 0x00002000,
+        eInitializedMask     = 0x00004000,
+        eValidMask           = 0x00008000,
+        eBindingMask         = 0x00010000
+    };
+    
+    void MarkBinding()         { mFlags |=  eBindingMask; }
+    void ClearBinding()        { mFlags &= ~eBindingMask; }
+    PRBool IsBinding()         { return (mFlags & eBindingMask) != 0; }
+
+    void MarkEntryDirty()      { mFlags |=  eEntryDirtyMask; }
+    void MarkEntryClean()      { mFlags &= ~eEntryDirtyMask; }
+    void MarkDataDirty()       { mFlags |=  eDataDirtyMask; }
+    void MarkDataClean()       { mFlags &= ~eDataDirtyMask; }
+    void MarkMetaDataDirty()   { mFlags |=  eMetaDataDirtyMask; }
+    void MarkMetaDataClean()   { mFlags &= ~eMetaDataDirtyMask; }
+    void MarkStreamData()      { mFlags |=  eStreamDataMask; }
+    void MarkValid()           { mFlags |=  eValidMask; }
+    void MarkInvalid()         { mFlags &= ~eValidMask; }
+    //    void MarkAllowedInMemory() { mFlags |=  eAllowedInMemoryMask; }
+    //    void MarkAllowedOnDisk()   { mFlags |=  eAllowedOnDiskMask; }
+
+    PRBool IsDoomed()          { return (mFlags & eDoomedMask) != 0; }
+    PRBool IsEntryDirty()      { return (mFlags & eEntryDirtyMask) != 0; }
+    PRBool IsDataDirty()       { return (mFlags & eDataDirtyMask) != 0; }
+    PRBool IsMetaDataDirty()   { return (mFlags & eMetaDataDirtyMask) != 0; }
+    PRBool IsStreamData()      { return (mFlags & eStreamDataMask) != 0; }
+    PRBool IsActive()          { return (mFlags & eActiveMask) != 0; }
+    PRBool IsInitialized()     { return (mFlags & eInitializedMask) != 0; }
+    PRBool IsValid()           { return (mFlags & eValidMask) != 0; }
+    PRBool IsInvalid()         { return (mFlags & eValidMask) == 0; }
+    PRBool IsInUse()           { return IsBinding() ||
+                                        !(PR_CLIST_IS_EMPTY(&mRequestQ) &&
+                                          PR_CLIST_IS_EMPTY(&mDescriptorQ)); }
+    PRBool IsNotInUse()        { return !IsInUse(); }
+
+
+    PRBool IsAllowedInMemory()
+    {
+        return (StoragePolicy() ==  nsICache::STORE_ANYWHERE) ||
+            (StoragePolicy() == nsICache::STORE_IN_MEMORY);
+    }
+
+    PRBool IsAllowedOnDisk()
+    {
+        return (StoragePolicy() == nsICache::STORE_ANYWHERE) ||
+            (StoragePolicy() == nsICache::STORE_ON_DISK) ||
+            (StoragePolicy() == nsICache::STORE_ON_DISK_AS_FILE);
+    }
+
+    PRBool IsAllowedOffline()
+    {
+        return (StoragePolicy() == nsICache::STORE_OFFLINE);
+    }
+
+    nsCacheStoragePolicy  StoragePolicy()
+    {
+        return (nsCacheStoragePolicy)(mFlags & eStoragePolicyMask);
+    }
+
+    void SetStoragePolicy(nsCacheStoragePolicy policy)
+    {
+        NS_ASSERTION(policy <= 0xFF, "too many bits in nsCacheStoragePolicy");
+        mFlags &= ~eStoragePolicyMask; // clear storage policy bits
+        mFlags |= policy;
+    }
+
+
+    // methods for nsCacheService
+    nsresult RequestAccess( nsCacheRequest * request, nsCacheAccessMode *accessGranted);
+    nsresult CreateDescriptor( nsCacheRequest *           request,
+                               nsCacheAccessMode          accessGranted,
+                               nsICacheEntryDescriptor ** result);
+
+    //    nsresult Open(nsCacheRequest *request, nsICacheEntryDescriptor ** result);
+    //    nsresult AsyncOpen(nsCacheRequest *request);
+    PRBool   RemoveRequest( nsCacheRequest * request);
+    PRBool   RemoveDescriptor( nsCacheEntryDescriptor * descriptor);
+    
+private:
+    friend class nsCacheEntryHashTable;
+    friend class nsCacheService;
+
+    void     DetachDescriptors(void);
+
+    // internal methods
+    void MarkDoomed()          { mFlags |=  eDoomedMask; }
+    void MarkStreamBased()     { mFlags |=  eStreamDataMask; }
+    void MarkInitialized()     { mFlags |=  eInitializedMask; }
+    void MarkActive()          { mFlags |=  eActiveMask; }
+    void MarkInactive()        { mFlags &= ~eActiveMask; }
+
+    nsCString *             mKey;            // 4  // XXX ask scc about const'ness
+    PRUint32                mFetchCount;     // 4
+    PRUint32                mLastFetched;    // 4
+    PRUint32                mLastModified;   // 4
+    PRUint32                mLastValidated;  // 4
+    PRUint32                mExpirationTime; // 4
+    PRUint32                mFlags;          // 4
+    PRUint32                mDataSize;       // 4
+    nsCacheDevice *         mCacheDevice;    // 4
+    nsCOMPtr<nsISupports>   mSecurityInfo;   // 
+    nsISupports *           mData;           // strong ref
+    nsCOMPtr<nsIThread>     mThread;
+    nsCacheMetaData         mMetaData;       // 4
+    PRCList                 mRequestQ;       // 8
+    PRCList                 mDescriptorQ;    // 8
+};
+
+
+/******************************************************************************
+* nsCacheEntryInfo
+*******************************************************************************/
+class nsCacheEntryInfo : public nsICacheEntryInfo {
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICACHEENTRYINFO
+
+    nsCacheEntryInfo(nsCacheEntry* entry)
+        :   mCacheEntry(entry)
+    {
+    }
+
+    virtual ~nsCacheEntryInfo() {}
+    void    DetachEntry() { mCacheEntry = nsnull; }
+    
+private:
+    nsCacheEntry * mCacheEntry;
+};
+
+
+/******************************************************************************
+* nsCacheEntryHashTable
+*******************************************************************************/
+typedef struct {
+    PLDHashNumber  keyHash;
+    nsCacheEntry  *cacheEntry;
+} nsCacheEntryHashTableEntry;
+
+
+class nsCacheEntryHashTable
+{
+public:
+    nsCacheEntryHashTable();
+    ~nsCacheEntryHashTable();
+
+    nsresult      Init();
+    void          Shutdown();
+
+    nsCacheEntry *GetEntry( const nsCString * key);
+    nsresult      AddEntry( nsCacheEntry *entry);
+    void          RemoveEntry( nsCacheEntry *entry);
+    
+    void          VisitEntries( PLDHashEnumerator etor, void *arg);
+
+private:
+    // PLDHashTable operation callbacks
+    static PLDHashNumber  HashKey( PLDHashTable *table, const void *key);
+
+    static PRBool         MatchEntry( PLDHashTable *           table,
+                                      const PLDHashEntryHdr *  entry,
+                                      const void *             key);
+
+    static void           MoveEntry( PLDHashTable *table,
+                                     const PLDHashEntryHdr *from,
+                                     PLDHashEntryHdr       *to);
+
+    static void           ClearEntry( PLDHashTable *table, PLDHashEntryHdr *entry);
+
+    static void           Finalize( PLDHashTable *table);
+
+    static
+    PLDHashOperator       FreeCacheEntries(PLDHashTable *    table,
+                                           PLDHashEntryHdr * hdr,
+                                           PRUint32          number,
+                                           void *            arg);
+    static
+    PLDHashOperator       VisitEntry(PLDHashTable *         table,
+                                     PLDHashEntryHdr *      hdr,
+                                     PRUint32               number,
+                                     void *                 arg);
+                                     
+    // member variables
+    static PLDHashTableOps ops;
+    PLDHashTable           table;
+    PRBool                 initialized;
+};
+
+#endif // _nsCacheEntry_h_
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheEntryDescriptor.cpp
@@ -0,0 +1,675 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheEntryDescriptor.cpp, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan, 22-February-2001
+ *
+ * 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 "nsICache.h"
+#include "nsCache.h"
+#include "nsCacheService.h"
+#include "nsCacheEntryDescriptor.h"
+#include "nsCacheEntry.h"
+#include "nsReadableUtils.h"
+#include "nsIOutputStream.h"
+#include "nsCRT.h"
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(nsCacheEntryDescriptor,
+                              nsICacheEntryDescriptor,
+                              nsICacheEntryInfo)
+
+nsCacheEntryDescriptor::nsCacheEntryDescriptor(nsCacheEntry * entry,
+                                               nsCacheAccessMode accessGranted)
+    : mCacheEntry(entry),
+      mAccessGranted(accessGranted)
+{
+    PR_INIT_CLIST(this);
+    NS_ADDREF(nsCacheService::GlobalInstance());  // ensure it lives for the lifetime of the descriptor
+}
+
+
+nsCacheEntryDescriptor::~nsCacheEntryDescriptor()
+{
+    // No need to close if the cache entry has already been severed.  This
+    // helps avoid a shutdown assertion (bug 285519) that is caused when
+    // consumers end up holding onto these objects past xpcom-shutdown.  It's
+    // okay for them to do that because the cache service calls our Close
+    // method during xpcom-shutdown, so we don't need to complain about it.
+    if (mCacheEntry)
+        Close();
+
+    nsCacheService * service = nsCacheService::GlobalInstance();
+    NS_RELEASE(service);
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetClientID(char ** result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    return ClientIDFromCacheKey(*(mCacheEntry->Key()), result);
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetDeviceID(char ** aDeviceID)
+{
+    NS_ENSURE_ARG_POINTER(aDeviceID);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    const char* deviceID = mCacheEntry->GetDeviceID();
+    if (!deviceID) {
+        *aDeviceID = nsnull;
+        return NS_OK;
+    }
+
+    *aDeviceID = NS_strdup(deviceID);
+    return *aDeviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetKey(nsACString &result)
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    return ClientKeyFromCacheKey(*(mCacheEntry->Key()), result);
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetFetchCount(PRInt32 *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *result = mCacheEntry->FetchCount();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetLastFetched(PRUint32 *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *result = mCacheEntry->LastFetched();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetLastModified(PRUint32 *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *result = mCacheEntry->LastModified();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetExpirationTime(PRUint32 *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *result = mCacheEntry->ExpirationTime();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::SetExpirationTime(PRUint32 expirationTime)
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    mCacheEntry->SetExpirationTime(expirationTime);
+    mCacheEntry->MarkEntryDirty();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP nsCacheEntryDescriptor::IsStreamBased(PRBool *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *result = mCacheEntry->IsStreamData();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP nsCacheEntryDescriptor::GetDataSize(PRUint32 *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *result = mCacheEntry->DataSize();
+    return NS_OK;
+}
+
+
+nsresult
+nsCacheEntryDescriptor::RequestDataSizeChange(PRInt32 deltaSize)
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    nsresult  rv;
+    rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize);
+    if (NS_SUCCEEDED(rv)) {
+        // XXX review for signed/unsigned math errors
+        PRUint32  newDataSize = mCacheEntry->DataSize() + deltaSize;
+        mCacheEntry->SetDataSize(newDataSize);
+        mCacheEntry->TouchData();
+    }
+    return rv;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::SetDataSize(PRUint32 dataSize)
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    // XXX review for signed/unsigned math errors
+    PRInt32  deltaSize = dataSize - mCacheEntry->DataSize();
+
+    nsresult  rv;
+    rv = nsCacheService::OnDataSizeChange(mCacheEntry, deltaSize);
+    // this had better be NS_OK, this call instance is advisory for memory cache objects
+    if (NS_SUCCEEDED(rv)) {
+        // XXX review for signed/unsigned math errors
+        PRUint32  newDataSize = mCacheEntry->DataSize() + deltaSize;
+        mCacheEntry->SetDataSize(newDataSize);
+        mCacheEntry->TouchData();
+    } else {
+        NS_WARNING("failed SetDataSize() on memory cache object!");
+    }
+    
+    return rv;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::OpenInputStream(PRUint32 offset, nsIInputStream ** result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+
+    {
+        nsCacheServiceAutoLock lock;
+        if (!mCacheEntry)                  return NS_ERROR_NOT_AVAILABLE;
+        if (!mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_NOT_STREAM;
+
+        // ensure valid permissions
+        if (!(mAccessGranted & nsICache::ACCESS_READ))
+            return NS_ERROR_CACHE_READ_ACCESS_DENIED;
+    }
+
+    nsInputStreamWrapper* cacheInput =
+        new nsInputStreamWrapper(this, offset);
+    if (!cacheInput) return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*result = cacheInput);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::OpenOutputStream(PRUint32 offset, nsIOutputStream ** result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+
+    {
+        nsCacheServiceAutoLock lock;
+        if (!mCacheEntry)                  return NS_ERROR_NOT_AVAILABLE;
+        if (!mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_NOT_STREAM;
+
+        // ensure valid permissions
+        if (!(mAccessGranted & nsICache::ACCESS_WRITE))
+            return NS_ERROR_CACHE_WRITE_ACCESS_DENIED;
+    }
+
+    nsOutputStreamWrapper* cacheOutput =
+        new nsOutputStreamWrapper(this, offset);
+    if (!cacheOutput) return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*result = cacheOutput);
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetCacheElement(nsISupports ** result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)                 return NS_ERROR_NOT_AVAILABLE;
+    if (mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_STREAM;
+
+    NS_IF_ADDREF(*result = mCacheEntry->Data());
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::SetCacheElement(nsISupports * cacheElement)
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)                 return NS_ERROR_NOT_AVAILABLE;
+    if (mCacheEntry->IsStreamData())  return NS_ERROR_CACHE_DATA_IS_STREAM;
+
+    return nsCacheService::SetCacheElement(mCacheEntry, cacheElement);
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetAccessGranted(nsCacheAccessMode *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    *result = mAccessGranted;
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetStoragePolicy(nsCacheStoragePolicy *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+    
+    *result = mCacheEntry->StoragePolicy();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::SetStoragePolicy(nsCacheStoragePolicy policy)
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+    // XXX validate policy against session?
+    
+    PRBool      storageEnabled = PR_FALSE;
+    storageEnabled = nsCacheService::IsStorageEnabledForPolicy_Locked(policy);
+    if (!storageEnabled)    return NS_ERROR_FAILURE;
+
+    // Don't change the storage policy of entries we can't write
+    if (!(mAccessGranted & nsICache::ACCESS_WRITE))
+        return NS_ERROR_NOT_AVAILABLE;
+    
+    // Don't allow a cache entry to move from memory-only to anything else
+    if (mCacheEntry->StoragePolicy() == nsICache::STORE_IN_MEMORY &&
+        policy != nsICache::STORE_IN_MEMORY)
+        return NS_ERROR_NOT_AVAILABLE;
+        
+    mCacheEntry->SetStoragePolicy(policy);
+    mCacheEntry->MarkEntryDirty();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetFile(nsIFile ** result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    return nsCacheService::GetFileForEntry(mCacheEntry, result);
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetSecurityInfo(nsISupports ** result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    *result = mCacheEntry->SecurityInfo();
+    NS_IF_ADDREF(*result);
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::SetSecurityInfo(nsISupports * securityInfo)
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    mCacheEntry->SetSecurityInfo(securityInfo);
+    mCacheEntry->MarkEntryDirty();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::Doom()
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    return nsCacheService::DoomEntry(mCacheEntry);
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::DoomAndFailPendingRequests(nsresult status)
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::MarkValid()
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    nsresult  rv = nsCacheService::ValidateEntry(mCacheEntry);
+    return rv;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::Close()
+{
+    nsCacheServiceAutoLock lock;
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    // XXX perhaps closing descriptors should clear/sever transports
+
+    // tell nsCacheService we're going away
+    nsCacheService::CloseDescriptor(this);
+    NS_ASSERTION(mCacheEntry == nsnull, "mCacheEntry not null");
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::GetMetaDataElement(const char *key, char **result)
+{
+    NS_ENSURE_ARG_POINTER(key);
+    *result = nsnull;
+
+    nsCacheServiceAutoLock lock;
+    NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
+
+    const char *value;
+
+    value = mCacheEntry->GetMetaDataElement(key);
+    if (!value) return NS_ERROR_NOT_AVAILABLE;
+
+    *result = NS_strdup(value);
+    if (!*result) return NS_ERROR_OUT_OF_MEMORY;
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::SetMetaDataElement(const char *key, const char *value)
+{
+    NS_ENSURE_ARG_POINTER(key);
+
+    nsCacheServiceAutoLock lock;
+    NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_AVAILABLE);
+
+    // XXX allow null value, for clearing key?
+
+    nsresult rv = mCacheEntry->SetMetaDataElement(key, value);
+    if (NS_SUCCEEDED(rv))
+        mCacheEntry->TouchMetaData();
+    return rv;
+}
+
+
+NS_IMETHODIMP
+nsCacheEntryDescriptor::VisitMetaData(nsICacheMetaDataVisitor * visitor)
+{
+    nsCacheServiceAutoLock lock;  // XXX check callers, we're calling out of module
+    NS_ENSURE_ARG_POINTER(visitor);
+    if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
+
+    return mCacheEntry->VisitMetaDataElements(visitor);
+}
+
+
+/******************************************************************************
+ * nsCacheInputStream - a wrapper for nsIInputstream keeps the cache entry
+ *                      open while referenced.
+ ******************************************************************************/
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsInputStreamWrapper,
+                              nsIInputStream)
+
+nsresult nsCacheEntryDescriptor::
+nsInputStreamWrapper::LazyInit()
+{
+    nsCacheServiceAutoLock lock;
+
+    nsCacheAccessMode mode;
+    nsresult rv = mDescriptor->GetAccessGranted(&mode);
+    if (NS_FAILED(rv)) return rv;
+
+    NS_ENSURE_TRUE(mode & nsICache::ACCESS_READ, NS_ERROR_UNEXPECTED);
+
+    nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
+    if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+    nsCOMPtr<nsIInputStream> input;
+    rv = nsCacheService::OpenInputStreamForEntry(cacheEntry, mode,
+                                                 mStartOffset,
+                                                 getter_AddRefs(mInput));
+    if (NS_FAILED(rv)) return rv;
+
+    mInitialized = PR_TRUE;
+    return NS_OK;
+}
+
+nsresult nsCacheEntryDescriptor::
+nsInputStreamWrapper::Close()
+{
+    nsresult rv = EnsureInit();
+    if (NS_FAILED(rv)) return rv;
+
+    return mInput->Close();
+}
+
+nsresult nsCacheEntryDescriptor::
+nsInputStreamWrapper::Available(PRUint32 *avail)
+{
+    nsresult rv = EnsureInit();
+    if (NS_FAILED(rv)) return rv;
+
+    return mInput->Available(avail);
+}
+
+nsresult nsCacheEntryDescriptor::
+nsInputStreamWrapper::Read(char *buf, PRUint32 count, PRUint32 *countRead)
+{
+    nsresult rv = EnsureInit();
+    if (NS_FAILED(rv)) return rv;
+
+    return mInput->Read(buf, count, countRead);
+}
+
+nsresult nsCacheEntryDescriptor::
+nsInputStreamWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure,
+                                   PRUint32 count, PRUint32 *countRead)
+{
+    // cache stream not buffered
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult nsCacheEntryDescriptor::
+nsInputStreamWrapper::IsNonBlocking(PRBool *result)
+{
+    // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
+    *result = PR_FALSE;
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ * nsCacheOutputStream - a wrapper for nsIOutputstream to track the amount of
+ *                       data written to a cache entry.
+ *                     - also keeps the cache entry open while referenced.
+ ******************************************************************************/
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheEntryDescriptor::nsOutputStreamWrapper,
+                              nsIOutputStream)
+
+nsresult nsCacheEntryDescriptor::
+nsOutputStreamWrapper::LazyInit()
+{
+    nsCacheServiceAutoLock lock;
+
+    nsCacheAccessMode mode;
+    nsresult rv = mDescriptor->GetAccessGranted(&mode);
+    if (NS_FAILED(rv)) return rv;
+
+    NS_ENSURE_TRUE(mode & nsICache::ACCESS_WRITE, NS_ERROR_UNEXPECTED);
+
+    nsCacheEntry* cacheEntry = mDescriptor->CacheEntry();
+    if (!cacheEntry) return NS_ERROR_NOT_AVAILABLE;
+
+    rv = nsCacheService::OpenOutputStreamForEntry(cacheEntry, mode, mStartOffset,
+                                                  getter_AddRefs(mOutput));
+    if (NS_FAILED(rv)) return rv;
+
+    nsCacheDevice* device = cacheEntry->CacheDevice();
+    if (!device) return NS_ERROR_NOT_AVAILABLE;
+
+    // the entry has been truncated to mStartOffset bytes, inform the device.
+    PRInt32 size = cacheEntry->DataSize();
+    rv = device->OnDataSizeChange(cacheEntry, mStartOffset - size);
+    if (NS_FAILED(rv)) return rv;
+
+    cacheEntry->SetDataSize(mStartOffset);
+
+    mInitialized = PR_TRUE;
+    return NS_OK;
+}
+
+nsresult nsCacheEntryDescriptor::
+nsOutputStreamWrapper::OnWrite(PRUint32 count)
+{
+    if (count > PR_INT32_MAX)  return NS_ERROR_UNEXPECTED;
+    return mDescriptor->RequestDataSizeChange((PRInt32)count);
+}
+
+NS_IMETHODIMP nsCacheEntryDescriptor::
+nsOutputStreamWrapper::Close()
+{
+    nsresult rv = EnsureInit();
+    if (NS_FAILED(rv)) return rv;
+
+    return mOutput->Close();
+}
+
+NS_IMETHODIMP nsCacheEntryDescriptor::
+nsOutputStreamWrapper::Flush()
+{
+    nsresult rv = EnsureInit();
+    if (NS_FAILED(rv)) return rv;
+
+    return mOutput->Flush();
+}
+
+NS_IMETHODIMP nsCacheEntryDescriptor::
+nsOutputStreamWrapper::Write(const char * buf,
+                             PRUint32     count,
+                             PRUint32 *   result)
+{
+    nsresult rv = EnsureInit();
+    if (NS_FAILED(rv)) return rv;
+
+    rv = OnWrite(count);
+    if (NS_FAILED(rv)) return rv;
+
+    return mOutput->Write(buf, count, result);
+}
+
+NS_IMETHODIMP nsCacheEntryDescriptor::
+nsOutputStreamWrapper::WriteFrom(nsIInputStream * inStr,
+                                 PRUint32         count,
+                                 PRUint32 *       result)
+{
+    NS_NOTREACHED("cache stream not buffered");
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsCacheEntryDescriptor::
+nsOutputStreamWrapper::WriteSegments(nsReadSegmentFun  reader,
+                                     void *            closure,
+                                     PRUint32          count,
+                                     PRUint32 *        result)
+{
+    NS_NOTREACHED("cache stream not buffered");
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsCacheEntryDescriptor::
+nsOutputStreamWrapper::IsNonBlocking(PRBool *result)
+{
+    // cache streams will never return NS_BASE_STREAM_WOULD_BLOCK
+    *result = PR_FALSE;
+    return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheEntryDescriptor.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheEntryDescriptor.h, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan, 22-February-2001
+ *
+ * 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 ***** */
+
+
+#ifndef _nsCacheEntryDescriptor_h_
+#define _nsCacheEntryDescriptor_h_
+
+#include "nsICacheEntryDescriptor.h"
+#include "nsCacheEntry.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+
+/******************************************************************************
+* nsCacheEntryDescriptor
+*******************************************************************************/
+class nsCacheEntryDescriptor :
+    public PRCList,
+    public nsICacheEntryDescriptor
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICACHEENTRYDESCRIPTOR
+    NS_DECL_NSICACHEENTRYINFO
+    
+    nsCacheEntryDescriptor(nsCacheEntry * entry, nsCacheAccessMode  mode);
+    virtual ~nsCacheEntryDescriptor();
+    
+    /**
+     * utility method to attempt changing data size of associated entry
+     */
+    nsresult  RequestDataSizeChange(PRInt32 deltaSize);
+    
+    /**
+     * methods callbacks for nsCacheService
+     */
+    nsCacheEntry * CacheEntry(void)      { return mCacheEntry; }
+    void           ClearCacheEntry(void) { mCacheEntry = nsnull; }
+
+private:
+
+
+     /*************************************************************************
+      * input stream wrapper class -
+      *
+      * The input stream wrapper references the descriptor, but the descriptor
+      * doesn't need any references to the stream wrapper.
+      *************************************************************************/
+     class nsInputStreamWrapper : public nsIInputStream {
+     private:
+         nsCacheEntryDescriptor    * mDescriptor;
+         nsCOMPtr<nsIInputStream>    mInput;
+         PRUint32                    mStartOffset;
+         PRBool                      mInitialized;
+     public:
+         NS_DECL_ISUPPORTS
+         NS_DECL_NSIINPUTSTREAM
+
+         nsInputStreamWrapper(nsCacheEntryDescriptor * desc, PRUint32 off)
+             : mDescriptor(desc)
+             , mStartOffset(off)
+             , mInitialized(PR_FALSE)
+         {
+             NS_ADDREF(mDescriptor);
+         }
+         virtual ~nsInputStreamWrapper()
+         {
+             NS_RELEASE(mDescriptor);
+         }
+
+     private:
+         nsresult LazyInit();
+         nsresult EnsureInit() { return mInitialized ? NS_OK : LazyInit(); }
+     };
+     friend class nsInputStreamWrapper;
+
+
+     /*************************************************************************
+      * output stream wrapper class -
+      *
+      * The output stream wrapper references the descriptor, but the descriptor
+      * doesn't need any references to the stream wrapper.
+      *************************************************************************/
+     class nsOutputStreamWrapper : public nsIOutputStream {
+     private:
+         nsCacheEntryDescriptor *    mDescriptor;
+         nsCOMPtr<nsIOutputStream>   mOutput;
+         PRUint32                    mStartOffset;
+         PRBool                      mInitialized;
+     public:
+         NS_DECL_ISUPPORTS
+         NS_DECL_NSIOUTPUTSTREAM
+
+         nsOutputStreamWrapper(nsCacheEntryDescriptor * desc, PRUint32 off)
+             : mDescriptor(desc)
+             , mStartOffset(off)
+             , mInitialized(PR_FALSE)
+         {
+             NS_ADDREF(mDescriptor); // owning ref
+         }
+         virtual ~nsOutputStreamWrapper()
+         { 
+             // XXX _HACK_ the storage stream needs this!
+             Close();
+             NS_RELEASE(mDescriptor);
+         }
+
+     private:
+         nsresult LazyInit();
+         nsresult EnsureInit() { return mInitialized ? NS_OK : LazyInit(); }
+         nsresult OnWrite(PRUint32 count);
+     };
+     friend class nsOutputStreamWrapper;
+
+ private:
+     /**
+      * nsCacheEntryDescriptor data members
+      */
+     nsCacheEntry          * mCacheEntry; // we are a child of the entry
+     nsCacheAccessMode       mAccessGranted;
+};
+
+
+#endif // _nsCacheEntryDescriptor_h_
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheMetaData.cpp
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheMetaData.cpp, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan <gordon@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 "nsCacheMetaData.h"
+#include "nsICacheEntryDescriptor.h"
+#include "prmem.h"
+
+const char *
+nsCacheMetaData::GetElement(const char * key)
+{
+    const char * data = mBuffer;
+    const char * limit = mBuffer + mMetaSize;
+
+    while (data < limit) {
+        // Point to the value part
+        const char * value = data + strlen(data) + 1;
+        if (strcmp(data, key) == 0)
+            return value;
+
+        // Skip value part
+        data = value + strlen(value) + 1;
+    }
+    return nsnull;
+}
+
+
+nsresult
+nsCacheMetaData::SetElement(const char * key,
+                            const char * value)
+{
+    const PRUint32 keySize = strlen(key) + 1;
+    char * pos = (char *)GetElement(key);
+
+    if (!value) {
+        // No value means remove the key/value pair completely, if existing
+        if (pos) {
+            PRUint32 oldValueSize = strlen(pos) + 1;
+            PRUint32 offset = pos - mBuffer;
+            PRUint32 remainder = mMetaSize - (offset + oldValueSize);
+
+            memmove(pos - keySize, pos + oldValueSize, remainder);
+            mMetaSize -= keySize + oldValueSize;
+        }
+        return NS_OK;
+    }
+
+    const PRUint32 valueSize = strlen(value) + 1;
+    PRUint32 newSize = mMetaSize + valueSize;
+    if (pos) {
+        const PRUint32 oldValueSize = strlen(pos) + 1;
+        const PRUint32 offset = pos - mBuffer;
+        const PRUint32 remainder = mMetaSize - (offset + oldValueSize);
+
+        // Update the value in place
+        newSize -= oldValueSize;
+        nsresult rv = EnsureBuffer(newSize);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        // Move the remainder to the right place
+        pos = mBuffer + offset;
+        memmove(pos + valueSize, pos + oldValueSize, remainder);
+    } else {
+        // allocate new meta data element
+        newSize += keySize;
+        nsresult rv = EnsureBuffer(newSize);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        // Add after last element
+        pos = mBuffer + mMetaSize;
+        memcpy(pos, key, keySize);
+        pos += keySize;
+    }
+
+    // Update value
+    memcpy(pos, value, valueSize);
+    mMetaSize = newSize;
+
+    return NS_OK;
+}
+
+nsresult
+nsCacheMetaData::FlattenMetaData(char * buffer, PRUint32 bufSize)
+{
+    if (mMetaSize > bufSize) {
+        NS_ERROR("buffer size too small for meta data.");
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    memcpy(buffer, mBuffer, mMetaSize);
+    return NS_OK;
+}
+
+nsresult
+nsCacheMetaData::UnflattenMetaData(const char * data, PRUint32 size)
+{
+    if (data && size) {
+        nsresult rv = EnsureBuffer(size);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        memcpy(mBuffer, data, size);
+        mMetaSize = size;
+    }
+    return NS_OK;
+}
+
+nsresult
+nsCacheMetaData::VisitElements(nsICacheMetaDataVisitor * visitor)
+{
+    const char * data = mBuffer;
+    const char * limit = mBuffer + mMetaSize;
+
+    while (data < limit) {
+        const char * key = data;
+        // Skip key part
+        data += strlen(data) + 1;
+        PRBool keepGoing;
+        nsresult rv = visitor->VisitMetaDataElement(key, data, &keepGoing);
+        if (NS_FAILED(rv) || !keepGoing)
+            break;
+
+        // Skip value part
+        data += strlen(data) + 1;
+    }
+    return NS_OK;
+}
+
+nsresult
+nsCacheMetaData::EnsureBuffer(PRUint32 bufSize)
+{
+    if (mBufferSize < bufSize) {
+        char * buf = (char *)PR_REALLOC(mBuffer, bufSize);
+        if (!buf) {
+            return NS_ERROR_OUT_OF_MEMORY;
+        }
+        mBuffer = buf;
+        mBufferSize = bufSize;
+    }
+    return NS_OK;
+}        
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheMetaData.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheMetaData.h, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan <gordon@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 ***** */
+
+#ifndef _nsCacheMetaData_h_
+#define _nsCacheMetaData_h_
+
+#include "nspr.h"
+#include "nscore.h"
+
+class nsICacheMetaDataVisitor;
+
+class nsCacheMetaData {
+public:
+    nsCacheMetaData() : mBuffer(nsnull), mBufferSize(0), mMetaSize(0) { }
+
+    ~nsCacheMetaData() {
+        mBufferSize = mMetaSize = 0;  
+        PR_FREEIF(mBuffer);
+    }
+
+    const char *  GetElement(const char * key);
+
+    nsresult      SetElement(const char * key, const char * value);
+
+    PRUint32      Size(void) { return mMetaSize; }
+
+    nsresult      FlattenMetaData(char * buffer, PRUint32 bufSize);
+
+    nsresult      UnflattenMetaData(const char * buffer, PRUint32 bufSize);
+
+    nsresult      VisitElements(nsICacheMetaDataVisitor * visitor);
+
+private:
+    nsresult      EnsureBuffer(PRUint32 size);
+
+    char *        mBuffer;
+    PRUint32      mBufferSize;
+    PRUint32      mMetaSize;
+};
+
+#endif // _nsCacheMetaData_h
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheRequest.h
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheRequest.h, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan, 22-February-2001
+ *
+ * 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 ***** */
+
+#ifndef _nsCacheRequest_h_
+#define _nsCacheRequest_h_
+
+#include "nspr.h"
+#include "nsCOMPtr.h"
+#include "nsICache.h"
+#include "nsICacheListener.h"
+#include "nsCacheSession.h"
+#include "nsCacheService.h"
+
+
+class nsCacheRequest : public PRCList
+{
+private:
+    friend class nsCacheService;
+    friend class nsCacheEntry;
+
+    nsCacheRequest( nsCString *           key, 
+                    nsICacheListener *    listener,
+                    nsCacheAccessMode     accessRequested,
+                    PRBool                blockingMode,
+                    nsCacheSession *      session)
+        : mKey(key),
+          mInfo(0),
+          mListener(listener),
+          mLock(nsnull),
+          mCondVar(nsnull)
+    {
+        MOZ_COUNT_CTOR(nsCacheRequest);
+        PR_INIT_CLIST(this);
+        SetAccessRequested(accessRequested);
+        SetStoragePolicy(session->StoragePolicy());
+        if (session->IsStreamBased())             MarkStreamBased();
+        if (session->WillDoomEntriesIfExpired())  MarkDoomEntriesIfExpired();
+        if (blockingMode == nsICache::BLOCKING)    MarkBlockingMode();
+        MarkWaitingForValidation();
+        NS_IF_ADDREF(mListener);
+    }
+    
+    ~nsCacheRequest()
+    {
+        MOZ_COUNT_DTOR(nsCacheRequest);
+        delete mKey;
+        if (mLock)    PR_DestroyLock(mLock);
+        if (mCondVar) PR_DestroyCondVar(mCondVar);
+        NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "request still on a list");
+
+        if (mListener)
+            nsCacheService::ReleaseObject_Locked(mListener, mThread);
+    }
+    
+    /**
+     * Simple Accessors
+     */
+    enum CacheRequestInfo {
+        eStoragePolicyMask         = 0x000000FF,
+        eStreamBasedMask           = 0x00000100,
+        eDoomEntriesIfExpiredMask  = 0x00001000,
+        eBlockingModeMask          = 0x00010000,
+        eWaitingForValidationMask  = 0x00100000,
+        eAccessRequestedMask       = 0xFF000000
+    };
+
+    void SetAccessRequested(nsCacheAccessMode mode)
+    {
+        NS_ASSERTION(mode <= 0xFF, "too many bits in nsCacheAccessMode");
+        mInfo &= ~eAccessRequestedMask;
+        mInfo |= mode << 24;
+    }
+
+    nsCacheAccessMode AccessRequested()
+    {
+        return (nsCacheAccessMode)((mInfo >> 24) & 0xFF);
+    }
+
+    void MarkStreamBased()      { mInfo |=  eStreamBasedMask; }
+    PRBool IsStreamBased()      { return (mInfo & eStreamBasedMask) != 0; }
+
+
+    void   MarkDoomEntriesIfExpired()   { mInfo |=  eDoomEntriesIfExpiredMask; }
+    PRBool WillDoomEntriesIfExpired()   { return (0 != (mInfo & eDoomEntriesIfExpiredMask)); }
+    
+    void   MarkBlockingMode()           { mInfo |= eBlockingModeMask; }
+    PRBool IsBlocking()                 { return (0 != (mInfo & eBlockingModeMask)); }
+    PRBool IsNonBlocking()              { return !(mInfo & eBlockingModeMask); }
+
+    void SetStoragePolicy(nsCacheStoragePolicy policy)
+    {
+        NS_ASSERTION(policy <= 0xFF, "too many bits in nsCacheStoragePolicy");
+        mInfo &= ~eStoragePolicyMask;  // clear storage policy bits
+        mInfo |= policy;         // or in new bits
+    }
+
+    nsCacheStoragePolicy StoragePolicy()
+    {
+        return (nsCacheStoragePolicy)(mInfo & 0xFF);
+    }
+
+    void   MarkWaitingForValidation() { mInfo |=  eWaitingForValidationMask; }
+    void   DoneWaitingForValidation() { mInfo &= ~eWaitingForValidationMask; }
+    PRBool WaitingForValidation()
+    {
+        return (mInfo & eWaitingForValidationMask) != 0;
+    }
+
+    nsresult
+    WaitForValidation(void)
+    {
+        if (!WaitingForValidation()) {   // flag already cleared
+            MarkWaitingForValidation();  // set up for next time
+            return NS_OK;                // early exit;
+        }
+
+        if (!mLock) {
+            mLock = PR_NewLock();
+            if (!mLock) return NS_ERROR_OUT_OF_MEMORY;
+
+            NS_ASSERTION(!mCondVar,"we have mCondVar, but didn't have mLock?");
+            mCondVar = PR_NewCondVar(mLock);
+            if (!mCondVar) {
+                PR_DestroyLock(mLock);
+                return NS_ERROR_OUT_OF_MEMORY;
+            }
+        }
+        PRStatus status = PR_SUCCESS;
+        PR_Lock(mLock);
+        while (WaitingForValidation() && (status == PR_SUCCESS) ) {
+            status = PR_WaitCondVar(mCondVar, PR_INTERVAL_NO_TIMEOUT);
+        }
+        MarkWaitingForValidation();  // set up for next time
+        PR_Unlock(mLock);
+        
+        NS_ASSERTION(status == PR_SUCCESS, "PR_WaitCondVar() returned PR_FAILURE?");
+        if (status == PR_FAILURE)
+            return NS_ERROR_UNEXPECTED;
+
+        return NS_OK;
+    }
+
+    void WakeUp(void) {
+        DoneWaitingForValidation();
+        if (mLock) {
+        PR_Lock(mLock);
+        PR_NotifyCondVar(mCondVar);
+        PR_Unlock(mLock);
+        }
+    }
+
+    /**
+     * Data members
+     */
+    nsCString *                mKey;
+    PRUint32                   mInfo;
+    nsICacheListener *         mListener;  // strong ref
+    nsCOMPtr<nsIThread>        mThread;
+    PRLock *                   mLock;
+    PRCondVar *                mCondVar;
+};
+
+#endif // _nsCacheRequest_h_
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheService.cpp
@@ -0,0 +1,2089 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheService.cpp, released
+ * February 10, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan, 10-February-2001
+ *   Michael Ventnor <m.ventnor@gmail.com>
+ *   Ehsan Akhgari <ehsan.akhgari@gmail.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 "necko-config.h"
+
+#include "nsCache.h"
+#include "nsCacheService.h"
+#include "nsCacheRequest.h"
+#include "nsCacheEntry.h"
+#include "nsCacheEntryDescriptor.h"
+#include "nsCacheDevice.h"
+#include "nsMemoryCacheDevice.h"
+#include "nsICacheVisitor.h"
+#include "nsDiskCacheDevice.h"
+
+#ifdef NECKO_OFFLINE_CACHE
+#include "nsDiskCacheDeviceSQL.h"
+#endif
+
+#include "nsIObserverService.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefBranch2.h"
+#include "nsILocalFile.h"
+#include "nsIOService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+#include "nsVoidArray.h"
+#include "nsDeleteDir.h"
+#include "nsIPrivateBrowsingService.h"
+#include "nsNetCID.h"
+#include <math.h>  // for log()
+#include "mozilla/Services.h"
+
+#include "mozilla/FunctionTimer.h"
+
+/******************************************************************************
+ * nsCacheProfilePrefObserver
+ *****************************************************************************/
+#ifdef XP_MAC
+#pragma mark nsCacheProfilePrefObserver
+#endif
+
+#define DISK_CACHE_ENABLE_PREF      "browser.cache.disk.enable"
+#define DISK_CACHE_DIR_PREF         "browser.cache.disk.parent_directory"
+#define DISK_CACHE_CAPACITY_PREF    "browser.cache.disk.capacity"
+#define DISK_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.disk.max_entry_size"
+#define DISK_CACHE_CAPACITY         51200
+
+#define OFFLINE_CACHE_ENABLE_PREF   "browser.cache.offline.enable"
+#define OFFLINE_CACHE_DIR_PREF      "browser.cache.offline.parent_directory"
+#define OFFLINE_CACHE_CAPACITY_PREF "browser.cache.offline.capacity"
+#define OFFLINE_CACHE_CAPACITY      512000
+
+#define MEMORY_CACHE_ENABLE_PREF    "browser.cache.memory.enable"
+#define MEMORY_CACHE_CAPACITY_PREF  "browser.cache.memory.capacity"
+#define MEMORY_CACHE_MAX_ENTRY_SIZE_PREF "browser.cache.memory.max_entry_size"
+
+static const char * observerList[] = { 
+    "profile-before-change",
+    "profile-after-change",
+    NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+    NS_PRIVATE_BROWSING_SWITCH_TOPIC
+};
+static const char * prefList[] = { 
+#ifdef NECKO_DISK_CACHE
+    DISK_CACHE_ENABLE_PREF,
+    DISK_CACHE_CAPACITY_PREF,
+    DISK_CACHE_DIR_PREF,
+#endif
+#ifdef NECKO_OFFLINE_CACHE
+    OFFLINE_CACHE_ENABLE_PREF,
+    OFFLINE_CACHE_CAPACITY_PREF,
+    OFFLINE_CACHE_DIR_PREF,
+#endif
+    MEMORY_CACHE_ENABLE_PREF,
+    MEMORY_CACHE_CAPACITY_PREF
+};
+
+class nsCacheProfilePrefObserver : public nsIObserver
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+
+    nsCacheProfilePrefObserver()
+        : mHaveProfile(PR_FALSE)
+        , mDiskCacheEnabled(PR_FALSE)
+        , mDiskCacheCapacity(0)
+        , mOfflineCacheEnabled(PR_FALSE)
+        , mOfflineCacheCapacity(0)
+        , mMemoryCacheEnabled(PR_TRUE)
+        , mMemoryCacheCapacity(-1)
+        , mInPrivateBrowsing(PR_FALSE)
+    {
+    }
+
+    virtual ~nsCacheProfilePrefObserver() {}
+    
+    nsresult        Install();
+    void            Remove();
+    nsresult        ReadPrefs(nsIPrefBranch* branch);
+    
+    PRBool          DiskCacheEnabled();
+    PRInt32         DiskCacheCapacity()         { return mDiskCacheCapacity; }
+    nsILocalFile *  DiskCacheParentDirectory()  { return mDiskCacheParentDirectory; }
+
+    PRBool          OfflineCacheEnabled();
+    PRInt32         OfflineCacheCapacity()         { return mOfflineCacheCapacity; }
+    nsILocalFile *  OfflineCacheParentDirectory()  { return mOfflineCacheParentDirectory; }
+    
+    PRBool          MemoryCacheEnabled();
+    PRInt32         MemoryCacheCapacity();
+
+private:
+    PRBool                  mHaveProfile;
+    
+    PRBool                  mDiskCacheEnabled;
+    PRInt32                 mDiskCacheCapacity; // in kilobytes
+    nsCOMPtr<nsILocalFile>  mDiskCacheParentDirectory;
+
+    PRBool                  mOfflineCacheEnabled;
+    PRInt32                 mOfflineCacheCapacity; // in kilobytes
+    nsCOMPtr<nsILocalFile>  mOfflineCacheParentDirectory;
+    
+    PRBool                  mMemoryCacheEnabled;
+    PRInt32                 mMemoryCacheCapacity; // in kilobytes
+
+    PRBool                  mInPrivateBrowsing;
+};
+
+NS_IMPL_ISUPPORTS1(nsCacheProfilePrefObserver, nsIObserver)
+
+
+nsresult
+nsCacheProfilePrefObserver::Install()
+{
+    // install profile-change observer
+    nsCOMPtr<nsIObserverService> observerService =
+        mozilla::services::GetObserverService();
+    if (!observerService)
+        return NS_ERROR_FAILURE;
+    
+    nsresult rv, rv2 = NS_OK;
+    for (unsigned int i=0; i<NS_ARRAY_LENGTH(observerList); i++) {
+        rv = observerService->AddObserver(this, observerList[i], PR_FALSE);
+        if (NS_FAILED(rv)) 
+            rv2 = rv;
+    }
+    
+    // install preferences observer
+    nsCOMPtr<nsIPrefBranch2> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (!branch) return NS_ERROR_FAILURE;
+
+    for (unsigned int i=0; i<NS_ARRAY_LENGTH(prefList); i++) {
+        rv = branch->AddObserver(prefList[i], this, PR_FALSE);
+        if (NS_FAILED(rv))
+            rv2 = rv;
+    }
+
+    // determine the initial status of the private browsing mode
+    nsCOMPtr<nsIPrivateBrowsingService> pbs =
+      do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
+    if (pbs)
+      pbs->GetPrivateBrowsingEnabled(&mInPrivateBrowsing);
+
+    // Determine if we have a profile already
+    //     Install() is called *after* the profile-after-change notification
+    //     when there is only a single profile, or it is specified on the
+    //     commandline at startup.
+    //     In that case, we detect the presence of a profile by the existence
+    //     of the NS_APP_USER_PROFILE_50_DIR directory.
+
+    nsCOMPtr<nsIFile> directory;
+    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                getter_AddRefs(directory));
+    if (NS_SUCCEEDED(rv))
+        mHaveProfile = PR_TRUE;
+
+    rv = ReadPrefs(branch);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return rv2;
+}
+
+
+void
+nsCacheProfilePrefObserver::Remove()
+{
+    // remove Observer Service observers
+    nsCOMPtr<nsIObserverService> obs =
+        mozilla::services::GetObserverService();
+    if (obs) {
+        for (unsigned int i=0; i<NS_ARRAY_LENGTH(observerList); i++) {
+            obs->RemoveObserver(this, observerList[i]);
+        }
+    }
+
+    // remove Pref Service observers
+    nsCOMPtr<nsIPrefBranch2> prefs =
+        do_GetService(NS_PREFSERVICE_CONTRACTID);
+    if (!prefs)
+        return;
+    for (unsigned int i=0; i<NS_ARRAY_LENGTH(prefList); i++)
+        prefs->RemoveObserver(prefList[i], this); // remove cache pref observers
+}
+
+
+NS_IMETHODIMP
+nsCacheProfilePrefObserver::Observe(nsISupports *     subject,
+                                    const char *      topic,
+                                    const PRUnichar * data_unicode)
+{
+    nsresult rv;
+    NS_ConvertUTF16toUTF8 data(data_unicode);
+    CACHE_LOG_ALWAYS(("Observe [topic=%s data=%s]\n", topic, data.get()));
+
+    if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
+        // xpcom going away, shutdown cache service
+        if (nsCacheService::GlobalInstance())
+            nsCacheService::GlobalInstance()->Shutdown();
+    
+    } else if (!strcmp("profile-before-change", topic)) {
+        // profile before change
+        mHaveProfile = PR_FALSE;
+
+        // XXX shutdown devices
+        nsCacheService::OnProfileShutdown(!strcmp("shutdown-cleanse",
+                                                  data.get()));
+        
+    } else if (!strcmp("profile-after-change", topic)) {
+        // profile after change
+        mHaveProfile = PR_TRUE;
+        nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+        ReadPrefs(branch);
+        nsCacheService::OnProfileChanged();
+    
+    } else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
+
+        // ignore pref changes until we're done switch profiles
+        if (!mHaveProfile)  return NS_OK;
+
+        nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
+        if (NS_FAILED(rv))  return rv;
+
+#ifdef NECKO_DISK_CACHE
+        // which preference changed?
+        if (!strcmp(DISK_CACHE_ENABLE_PREF, data.get())) {
+
+            if (!mInPrivateBrowsing) {
+                rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
+                                         &mDiskCacheEnabled);
+                if (NS_FAILED(rv))  return rv;
+                nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
+            }
+
+        } else if (!strcmp(DISK_CACHE_CAPACITY_PREF, data.get())) {
+
+            PRInt32 capacity = 0;
+            rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity);
+            if (NS_FAILED(rv))  return rv;
+            mDiskCacheCapacity = PR_MAX(0, capacity);
+            nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
+#if 0            
+        } else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
+            // XXX We probaby don't want to respond to this pref except after
+            // XXX profile changes.  Ideally, there should be somekind of user
+            // XXX notification that the pref change won't take effect until
+            // XXX the next time the profile changes (browser launch)
+#endif            
+        } else 
+#endif // !NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+        // which preference changed?
+        if (!strcmp(OFFLINE_CACHE_ENABLE_PREF, data.get())) {
+
+            if (!mInPrivateBrowsing) {
+                rv = branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
+                                         &mOfflineCacheEnabled);
+                if (NS_FAILED(rv))  return rv;
+                nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
+            }
+
+        } else if (!strcmp(OFFLINE_CACHE_CAPACITY_PREF, data.get())) {
+
+            PRInt32 capacity = 0;
+            rv = branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF, &capacity);
+            if (NS_FAILED(rv))  return rv;
+            mOfflineCacheCapacity = PR_MAX(0, capacity);
+            nsCacheService::SetOfflineCacheCapacity(mOfflineCacheCapacity);
+#if 0
+        } else if (!strcmp(OFFLINE_CACHE_DIR_PREF, data.get())) {
+            // XXX We probaby don't want to respond to this pref except after
+            // XXX profile changes.  Ideally, there should be some kind of user
+            // XXX notification that the pref change won't take effect until
+            // XXX the next time the profile changes (browser launch)
+#endif
+        } else
+#endif // !NECKO_OFFLINE_CACHE
+
+        if (!strcmp(MEMORY_CACHE_ENABLE_PREF, data.get())) {
+
+            rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
+                                     &mMemoryCacheEnabled);
+            if (NS_FAILED(rv))  return rv;
+            nsCacheService::SetMemoryCache();
+            
+        } else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) {
+
+            mMemoryCacheCapacity = -1;
+            (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
+                                      &mMemoryCacheCapacity);
+            nsCacheService::SetMemoryCache();
+        }
+    } else if (!strcmp(NS_PRIVATE_BROWSING_SWITCH_TOPIC, topic)) {
+        if (!strcmp(NS_PRIVATE_BROWSING_ENTER, data.get())) {
+            mInPrivateBrowsing = PR_TRUE;
+
+            nsCacheService::OnEnterExitPrivateBrowsing();
+
+#ifdef NECKO_DISK_CACHE
+            mDiskCacheEnabled = PR_FALSE;
+            nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
+#endif // !NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+            mOfflineCacheEnabled = PR_FALSE;
+            nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
+#endif // !NECKO_OFFLINE_CACHE
+        } else if (!strcmp(NS_PRIVATE_BROWSING_LEAVE, data.get())) {
+            mInPrivateBrowsing = PR_FALSE;
+
+            nsCacheService::OnEnterExitPrivateBrowsing();
+
+#if defined(NECKO_DISK_CACHE) || defined(NECKO_OFFLINE_CACHE)
+            nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+            if (NS_FAILED(rv))  return rv;
+#endif // !NECKO_DISK_CACHE && !NECKO_OFFLINE_CACHE
+
+#ifdef NECKO_DISK_CACHE
+            mDiskCacheEnabled = PR_TRUE; // by default enabled
+            (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
+                                       &mDiskCacheEnabled);
+            nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
+#endif // !NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+            mOfflineCacheEnabled = PR_TRUE; // by default enabled
+            (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
+                                       &mOfflineCacheEnabled);
+            nsCacheService::SetOfflineCacheEnabled(OfflineCacheEnabled());
+#endif // !NECKO_OFFLINE_CACHE
+        }
+    }
+    
+    return NS_OK;
+}
+
+
+nsresult
+nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
+{
+    nsresult rv = NS_OK;
+
+#ifdef NECKO_DISK_CACHE
+    // read disk cache device prefs
+    if (!mInPrivateBrowsing) {
+        mDiskCacheEnabled = PR_TRUE;  // presume disk cache is enabled
+        (void) branch->GetBoolPref(DISK_CACHE_ENABLE_PREF, &mDiskCacheEnabled);
+    }
+
+    mDiskCacheCapacity = DISK_CACHE_CAPACITY;
+    (void)branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &mDiskCacheCapacity);
+    mDiskCacheCapacity = PR_MAX(0, mDiskCacheCapacity);
+
+    (void) branch->GetComplexValue(DISK_CACHE_DIR_PREF,     // ignore error
+                                   NS_GET_IID(nsILocalFile),
+                                   getter_AddRefs(mDiskCacheParentDirectory));
+    
+    if (!mDiskCacheParentDirectory) {
+        nsCOMPtr<nsIFile>  directory;
+
+        // try to get the disk cache parent directory
+        rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
+                                    getter_AddRefs(directory));
+        if (NS_FAILED(rv)) {
+            // try to get the profile directory (there may not be a profile yet)
+            nsCOMPtr<nsIFile> profDir;
+            NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                   getter_AddRefs(profDir));
+            NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
+                                   getter_AddRefs(directory));
+            if (!directory)
+                directory = profDir;
+            else if (profDir) {
+                PRBool same;
+                if (NS_SUCCEEDED(profDir->Equals(directory, &same)) && !same) {
+                    // We no longer store the cache directory in the main
+                    // profile directory, so we should cleanup the old one.
+                    rv = profDir->AppendNative(NS_LITERAL_CSTRING("Cache"));
+                    if (NS_SUCCEEDED(rv)) {
+                        PRBool exists;
+                        if (NS_SUCCEEDED(profDir->Exists(&exists)) && exists)
+                            DeleteDir(profDir, PR_FALSE, PR_FALSE);
+                    }
+                }
+            }
+        }
+        // use file cache in build tree only if asked, to avoid cache dir litter
+        if (!directory && PR_GetEnv("NECKO_DEV_ENABLE_DISK_CACHE")) {
+            rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
+                                        getter_AddRefs(directory));
+        }
+        if (directory)
+            mDiskCacheParentDirectory = do_QueryInterface(directory, &rv);
+    }
+#endif // !NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+    // read offline cache device prefs
+    if (!mInPrivateBrowsing) {
+        mOfflineCacheEnabled = PR_TRUE;  // presume offline cache is enabled
+        (void) branch->GetBoolPref(OFFLINE_CACHE_ENABLE_PREF,
+                                   &mOfflineCacheEnabled);
+    }
+
+    mOfflineCacheCapacity = OFFLINE_CACHE_CAPACITY;
+    (void)branch->GetIntPref(OFFLINE_CACHE_CAPACITY_PREF,
+                             &mOfflineCacheCapacity);
+    mOfflineCacheCapacity = PR_MAX(0, mOfflineCacheCapacity);
+
+    (void) branch->GetComplexValue(OFFLINE_CACHE_DIR_PREF,     // ignore error
+                                   NS_GET_IID(nsILocalFile),
+                                   getter_AddRefs(mOfflineCacheParentDirectory));
+
+    if (!mOfflineCacheParentDirectory) {
+        nsCOMPtr<nsIFile>  directory;
+
+        // try to get the offline cache parent directory
+        rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
+                                    getter_AddRefs(directory));
+        if (NS_FAILED(rv)) {
+            // try to get the profile directory (there may not be a profile yet)
+            nsCOMPtr<nsIFile> profDir;
+            NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                   getter_AddRefs(profDir));
+            NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
+                                   getter_AddRefs(directory));
+            if (!directory)
+                directory = profDir;
+        }
+#if DEBUG
+        if (!directory) {
+            // use current process directory during development
+            rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR,
+                                        getter_AddRefs(directory));
+        }
+#endif
+        if (directory)
+            mOfflineCacheParentDirectory = do_QueryInterface(directory, &rv);
+    }
+#endif // !NECKO_OFFLINE_CACHE
+    
+    // read memory cache device prefs
+    (void) branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF, &mMemoryCacheEnabled);
+
+    mMemoryCacheCapacity = -1;
+    (void) branch->GetIntPref(MEMORY_CACHE_CAPACITY_PREF,
+                              &mMemoryCacheCapacity);
+        
+    return rv;
+}
+
+
+PRBool
+nsCacheProfilePrefObserver::DiskCacheEnabled()
+{
+    if ((mDiskCacheCapacity == 0) || (!mDiskCacheParentDirectory))  return PR_FALSE;
+    return mDiskCacheEnabled;
+}
+
+
+PRBool
+nsCacheProfilePrefObserver::OfflineCacheEnabled()
+{
+    if ((mOfflineCacheCapacity == 0) || (!mOfflineCacheParentDirectory))
+        return PR_FALSE;
+
+    return mOfflineCacheEnabled;
+}
+
+
+PRBool
+nsCacheProfilePrefObserver::MemoryCacheEnabled()
+{
+    if (mMemoryCacheCapacity == 0)  return PR_FALSE;
+    return mMemoryCacheEnabled;
+}
+
+
+/**
+ * MemoryCacheCapacity
+ *
+ * If the browser.cache.memory.capacity preference is positive, we use that
+ * value for the amount of memory available for the cache.
+ *
+ * If browser.cache.memory.capacity is zero, the memory cache is disabled.
+ * 
+ * If browser.cache.memory.capacity is negative or not present, we use a
+ * formula that grows less than linearly with the amount of system memory, 
+ * with an upper limit on the cache size. No matter how much physical RAM is
+ * present, the default cache size would not exceed 32 MB. This maximum would
+ * apply only to systems with more than 4 GB of RAM (e.g. terminal servers)
+ *
+ *   RAM   Cache
+ *   ---   -----
+ *   32 Mb   2 Mb
+ *   64 Mb   4 Mb
+ *  128 Mb   6 Mb
+ *  256 Mb  10 Mb
+ *  512 Mb  14 Mb
+ * 1024 Mb  18 Mb
+ * 2048 Mb  24 Mb
+ * 4096 Mb  30 Mb
+ *
+ * The equation for this is (for cache size C and memory size K (kbytes)):
+ *  x = log2(K) - 14
+ *  C = x^2/3 + x + 2/3 + 0.1 (0.1 for rounding)
+ *  if (C > 32) C = 32
+ */
+
+PRInt32
+nsCacheProfilePrefObserver::MemoryCacheCapacity()
+{
+    PRInt32 capacity = mMemoryCacheCapacity;
+    if (capacity >= 0) {
+        CACHE_LOG_DEBUG(("Memory cache capacity forced to %d\n", capacity));
+        return capacity;
+    }
+
+    static PRUint64 bytes = PR_GetPhysicalMemorySize();
+    CACHE_LOG_DEBUG(("Physical Memory size is %llu\n", bytes));
+
+    // If getting the physical memory failed, arbitrarily assume
+    // 32 MB of RAM. We use a low default to have a reasonable
+    // size on all the devices we support.
+    if (bytes == 0)
+        bytes = 32 * 1024 * 1024;
+
+    // Conversion from unsigned int64 to double doesn't work on all platforms.
+    // We need to truncate the value at LL_MAXINT to make sure we don't
+    // overflow.
+    if (LL_CMP(bytes, >, LL_MAXINT))
+        bytes = LL_MAXINT;
+
+    PRUint64 kbytes;
+    LL_SHR(kbytes, bytes, 10);
+
+    double kBytesD;
+    LL_L2D(kBytesD, (PRInt64) kbytes);
+
+    double x = log(kBytesD)/log(2.0) - 14;
+    if (x > 0) {
+        capacity = (PRInt32)(x * x / 3.0 + x + 2.0 / 3 + 0.1); // 0.1 for rounding
+        if (capacity > 32)
+            capacity = 32;
+        capacity   *= 1024;
+    } else {
+        capacity    = 0;
+    }
+
+    return capacity;
+}
+
+/******************************************************************************
+ * nsCacheService
+ *****************************************************************************/
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark nsCacheService
+#endif
+
+nsCacheService *   nsCacheService::gService = nsnull;
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsCacheService, nsICacheService)
+
+nsCacheService::nsCacheService()
+    : mLock(nsnull),
+      mInitialized(PR_FALSE),
+      mEnableMemoryDevice(PR_TRUE),
+      mEnableDiskDevice(PR_TRUE),
+      mMemoryDevice(nsnull),
+      mDiskDevice(nsnull),
+      mOfflineDevice(nsnull),
+      mTotalEntries(0),
+      mCacheHits(0),
+      mCacheMisses(0),
+      mMaxKeyLength(0),
+      mMaxDataSize(0),
+      mMaxMetaSize(0),
+      mDeactivateFailures(0),
+      mDeactivatedUnboundEntries(0)
+{
+    NS_ASSERTION(gService==nsnull, "multiple nsCacheService instances!");
+    gService = this;
+
+    // create list of cache devices
+    PR_INIT_CLIST(&mDoomedEntries);
+  
+    // allocate service lock
+    mLock = PR_NewLock();
+
+#if defined(DEBUG)
+    mLockedThread = nsnull;
+#endif
+}
+
+nsCacheService::~nsCacheService()
+{
+    if (mInitialized) // Shutdown hasn't been called yet.
+        (void) Shutdown();
+
+    PR_DestroyLock(mLock);
+    gService = nsnull;
+}
+
+
+nsresult
+nsCacheService::Init()
+{
+    NS_TIME_FUNCTION;
+
+    NS_ASSERTION(!mInitialized, "nsCacheService already initialized.");
+    if (mInitialized)
+        return NS_ERROR_ALREADY_INITIALIZED;
+
+    if (mLock == nsnull)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    CACHE_LOG_INIT();
+
+    // initialize hashtable for active cache entries
+    nsresult rv = mActiveEntries.Init();
+    if (NS_FAILED(rv)) return rv;
+    
+    // create profile/preference observer
+    mObserver = new nsCacheProfilePrefObserver();
+    if (!mObserver)  return NS_ERROR_OUT_OF_MEMORY;
+    NS_ADDREF(mObserver);
+    
+    mObserver->Install();
+    mEnableDiskDevice    = mObserver->DiskCacheEnabled();
+    mEnableOfflineDevice = mObserver->OfflineCacheEnabled();
+    mEnableMemoryDevice  = mObserver->MemoryCacheEnabled();
+
+    mInitialized = PR_TRUE;
+    return NS_OK;
+}
+
+
+void
+nsCacheService::Shutdown()
+{
+    nsCacheServiceAutoLock lock;
+    NS_ASSERTION(mInitialized, 
+                 "can't shutdown nsCacheService unless it has been initialized.");
+
+    if (mInitialized) {
+
+        mInitialized = PR_FALSE;
+
+        mObserver->Remove();
+        NS_RELEASE(mObserver);
+        
+        // Clear entries
+        ClearDoomList();
+        ClearActiveEntries();
+
+        // deallocate memory and disk caches
+        delete mMemoryDevice;
+        mMemoryDevice = nsnull;
+
+#ifdef NECKO_DISK_CACHE
+        delete mDiskDevice;
+        mDiskDevice = nsnull;
+#endif // !NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+        NS_IF_RELEASE(mOfflineDevice);
+#endif // !NECKO_OFFLINE_CACHE
+
+#if defined(NECKO_DISK_CACHE) && defined(PR_LOGGING)
+        LogCacheStatistics();
+#endif
+    }
+}
+
+
+NS_METHOD
+nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
+{
+    nsresult  rv;
+
+    if (aOuter != nsnull)
+        return NS_ERROR_NO_AGGREGATION;
+
+    nsCacheService * cacheService = new nsCacheService();
+    if (cacheService == nsnull)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(cacheService);
+    rv = cacheService->Init();
+    if (NS_SUCCEEDED(rv)) {
+        rv = cacheService->QueryInterface(aIID, aResult);
+    }
+    NS_RELEASE(cacheService);
+    return rv;
+}
+
+
+NS_IMETHODIMP
+nsCacheService::CreateSession(const char *          clientID,
+                              nsCacheStoragePolicy  storagePolicy, 
+                              PRBool                streamBased,
+                              nsICacheSession     **result)
+{
+    *result = nsnull;
+
+    if (this == nsnull)  return NS_ERROR_NOT_AVAILABLE;
+
+    nsCacheSession * session = new nsCacheSession(clientID, storagePolicy, streamBased);
+    if (!session)  return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(*result = session);
+
+    return NS_OK;
+}
+
+
+nsresult
+nsCacheService::EvictEntriesForSession(nsCacheSession * session)
+{
+    NS_ASSERTION(gService, "nsCacheService::gService is null.");
+    return gService->EvictEntriesForClient(session->ClientID()->get(),
+                                 session->StoragePolicy());
+}
+
+
+nsresult
+nsCacheService::EvictEntriesForClient(const char *          clientID,
+                                      nsCacheStoragePolicy  storagePolicy)
+{
+    if (this == nsnull) return NS_ERROR_NOT_AVAILABLE; // XXX eh?
+
+    nsCOMPtr<nsIObserverService> obsSvc =
+        mozilla::services::GetObserverService();
+    if (obsSvc) {
+        // Proxy to the UI thread since the observer service isn't thredsafe.
+        // We use an async proxy, since this it's not important whether this
+        // notification happens before or after the actual eviction.
+
+        nsCOMPtr<nsIObserverService> obsProxy;
+        NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
+                             NS_GET_IID(nsIObserverService), obsSvc,
+                             NS_PROXY_ASYNC, getter_AddRefs(obsProxy));
+
+        if (obsProxy) {
+            obsProxy->NotifyObservers(this,
+                                      NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID,
+                                      nsnull);
+        }
+    }
+
+    nsCacheServiceAutoLock lock;
+    nsresult res = NS_OK;
+
+#ifdef NECKO_DISK_CACHE
+    if (storagePolicy == nsICache::STORE_ANYWHERE ||
+        storagePolicy == nsICache::STORE_ON_DISK) {
+
+        if (mEnableDiskDevice) {
+            nsresult rv;
+            if (!mDiskDevice)
+                rv = CreateDiskDevice();
+            if (mDiskDevice)
+                rv = mDiskDevice->EvictEntries(clientID);
+            if (NS_FAILED(rv)) res = rv;
+        }
+    }
+#endif // ! NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+    // Only clear the offline cache if it has been specifically asked for.
+    if (storagePolicy == nsICache::STORE_OFFLINE) {
+        if (mEnableOfflineDevice) {
+            nsresult rv;
+            if (!mOfflineDevice)
+                rv = CreateOfflineDevice();
+            if (mOfflineDevice)
+                rv = mOfflineDevice->EvictEntries(clientID);
+            if (NS_FAILED(rv)) res = rv;
+        }
+    }
+#endif // ! NECKO_OFFLINE_CACHE
+
+    if (storagePolicy == nsICache::STORE_ANYWHERE ||
+        storagePolicy == nsICache::STORE_IN_MEMORY) {
+
+        // If there is no memory device, there is no need to evict it...
+        if (mMemoryDevice) {
+            nsresult rv;
+            rv = mMemoryDevice->EvictEntries(clientID);
+            if (NS_FAILED(rv)) res = rv;
+        }
+    }
+
+    return res;
+}
+
+
+nsresult        
+nsCacheService::IsStorageEnabledForPolicy(nsCacheStoragePolicy  storagePolicy,
+                                          PRBool *              result)
+{
+    if (gService == nsnull) return NS_ERROR_NOT_AVAILABLE;
+    nsCacheServiceAutoLock lock;
+
+    *result = gService->IsStorageEnabledForPolicy_Locked(storagePolicy);
+    return NS_OK;
+}
+
+
+PRBool        
+nsCacheService::IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy  storagePolicy)
+{
+    if (gService->mEnableMemoryDevice &&
+        (storagePolicy == nsICache::STORE_ANYWHERE ||
+         storagePolicy == nsICache::STORE_IN_MEMORY)) {
+        return PR_TRUE;
+    }
+    if (gService->mEnableDiskDevice &&
+        (storagePolicy == nsICache::STORE_ANYWHERE ||
+         storagePolicy == nsICache::STORE_ON_DISK  ||
+         storagePolicy == nsICache::STORE_ON_DISK_AS_FILE)) {
+        return PR_TRUE;
+    }
+    if (gService->mEnableOfflineDevice &&
+        storagePolicy == nsICache::STORE_OFFLINE) {
+        return PR_TRUE;
+    }
+    
+    return PR_FALSE;
+}
+
+NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
+{
+    NS_ENSURE_ARG_POINTER(visitor);
+
+    nsCacheServiceAutoLock lock;
+
+    if (!(mEnableDiskDevice || mEnableMemoryDevice))
+        return NS_ERROR_NOT_AVAILABLE;
+
+    // XXX record the fact that a visitation is in progress, 
+    // XXX i.e. keep list of visitors in progress.
+    
+    nsresult rv = NS_OK;
+    // If there is no memory device, there are then also no entries to visit...
+    if (mMemoryDevice) {
+        rv = mMemoryDevice->Visit(visitor);
+        if (NS_FAILED(rv)) return rv;
+    }
+
+#ifdef NECKO_DISK_CACHE
+    if (mEnableDiskDevice) {
+        if (!mDiskDevice) {
+            rv = CreateDiskDevice();
+            if (NS_FAILED(rv)) return rv;
+        }
+        rv = mDiskDevice->Visit(visitor);
+        if (NS_FAILED(rv)) return rv;
+    }
+#endif // !NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+    if (mEnableOfflineDevice) {
+        if (!mOfflineDevice) {
+            rv = CreateOfflineDevice();
+            if (NS_FAILED(rv)) return rv;
+        }
+        rv = mOfflineDevice->Visit(visitor);
+        if (NS_FAILED(rv)) return rv;
+    }
+#endif // !NECKO_OFFLINE_CACHE
+
+    // XXX notify any shutdown process that visitation is complete for THIS visitor.
+    // XXX keep queue of visitors
+
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP nsCacheService::EvictEntries(nsCacheStoragePolicy storagePolicy)
+{
+    return  EvictEntriesForClient(nsnull, storagePolicy);
+}
+
+/**
+ * Internal Methods
+ */
+nsresult
+nsCacheService::CreateDiskDevice()
+{
+#ifdef NECKO_DISK_CACHE
+    if (!mInitialized)      return NS_ERROR_NOT_AVAILABLE;
+    if (!mEnableDiskDevice) return NS_ERROR_NOT_AVAILABLE;
+    if (mDiskDevice)        return NS_OK;
+
+    mDiskDevice = new nsDiskCacheDevice;
+    if (!mDiskDevice)       return NS_ERROR_OUT_OF_MEMORY;
+
+    // set the preferences
+    mDiskDevice->SetCacheParentDirectory(mObserver->DiskCacheParentDirectory());
+    mDiskDevice->SetCapacity(mObserver->DiskCacheCapacity());
+    
+    nsresult rv = mDiskDevice->Init();
+    if (NS_FAILED(rv)) {
+#if DEBUG
+        printf("###\n");
+        printf("### mDiskDevice->Init() failed (0x%.8x)\n", rv);
+        printf("###    - disabling disk cache for this session.\n");
+        printf("###\n");
+#endif        
+        mEnableDiskDevice = PR_FALSE;
+        delete mDiskDevice;
+        mDiskDevice = nsnull;
+    }
+    return rv;
+#else // !NECKO_DISK_CACHE
+    NS_NOTREACHED("nsCacheService::CreateDiskDevice");
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult
+nsCacheService::CreateOfflineDevice()
+{
+#ifdef NECKO_OFFLINE_CACHE
+    CACHE_LOG_ALWAYS(("Creating offline device"));
+
+    if (!mInitialized)         return NS_ERROR_NOT_AVAILABLE;
+    if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
+    if (mOfflineDevice)        return NS_OK;
+
+    mOfflineDevice = new nsOfflineCacheDevice;
+    if (!mOfflineDevice)       return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_ADDREF(mOfflineDevice);
+
+    // set the preferences
+    mOfflineDevice->SetCacheParentDirectory(
+        mObserver->OfflineCacheParentDirectory());
+    mOfflineDevice->SetCapacity(mObserver->OfflineCacheCapacity());
+
+    nsresult rv = mOfflineDevice->Init();
+    if (NS_FAILED(rv)) {
+        CACHE_LOG_DEBUG(("mOfflineDevice->Init() failed (0x%.8x)\n", rv));
+        CACHE_LOG_DEBUG(("    - disabling offline cache for this session.\n"));
+
+        mEnableOfflineDevice = PR_FALSE;
+        NS_RELEASE(mOfflineDevice);
+    }
+    return rv;
+#else // !NECKO_DISK_CACHE
+    NS_NOTREACHED("nsCacheService::CreateOfflineDevice");
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult
+nsCacheService::CreateMemoryDevice()
+{
+    if (!mInitialized)        return NS_ERROR_NOT_AVAILABLE;
+    if (!mEnableMemoryDevice) return NS_ERROR_NOT_AVAILABLE;
+    if (mMemoryDevice)        return NS_OK;
+
+    mMemoryDevice = new nsMemoryCacheDevice;
+    if (!mMemoryDevice)       return NS_ERROR_OUT_OF_MEMORY;
+    
+    // set preference
+    PRInt32 capacity = mObserver->MemoryCacheCapacity();
+    CACHE_LOG_DEBUG(("Creating memory device with capacity %d\n", capacity));
+    mMemoryDevice->SetCapacity(capacity);
+
+    nsresult rv = mMemoryDevice->Init();
+    if (NS_FAILED(rv)) {
+        NS_WARNING("Initialization of Memory Cache failed.");
+        delete mMemoryDevice;
+        mMemoryDevice = nsnull;
+    }
+    return rv;
+}
+
+
+nsresult
+nsCacheService::CreateRequest(nsCacheSession *   session,
+                              const nsACString & clientKey,
+                              nsCacheAccessMode  accessRequested,
+                              PRBool             blockingMode,
+                              nsICacheListener * listener,
+                              nsCacheRequest **  request)
+{
+    NS_ASSERTION(request, "CreateRequest: request is null");
+     
+    nsCString * key = new nsCString(*session->ClientID());
+    if (!key)
+        return NS_ERROR_OUT_OF_MEMORY;
+    key->Append(':');
+    key->Append(clientKey);
+
+    if (mMaxKeyLength < key->Length()) mMaxKeyLength = key->Length();
+
+    // create request
+    *request = new  nsCacheRequest(key, listener, accessRequested, blockingMode, session);    
+    if (!*request) {
+        delete key;
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    if (!listener)  return NS_OK;  // we're sync, we're done.
+
+    // get the request's thread
+    (*request)->mThread = do_GetCurrentThread();
+    
+    return NS_OK;
+}
+
+
+class nsCacheListenerEvent : public nsRunnable
+{
+public:
+    nsCacheListenerEvent(nsICacheListener *listener,
+                         nsICacheEntryDescriptor *descriptor,
+                         nsCacheAccessMode accessGranted,
+                         nsresult status)
+        : mListener(listener)      // transfers reference
+        , mDescriptor(descriptor)  // transfers reference (may be null)
+        , mAccessGranted(accessGranted)
+        , mStatus(status)
+    {}
+
+    NS_IMETHOD Run()
+    {
+        mListener->OnCacheEntryAvailable(mDescriptor, mAccessGranted, mStatus);
+
+        NS_RELEASE(mListener);
+        NS_IF_RELEASE(mDescriptor);
+        return NS_OK;
+    }
+
+private:
+    // We explicitly leak mListener or mDescriptor if Run is not called
+    // because otherwise we cannot guarantee that they are destroyed on
+    // the right thread.
+
+    nsICacheListener        *mListener;
+    nsICacheEntryDescriptor *mDescriptor;
+    nsCacheAccessMode        mAccessGranted;
+    nsresult                 mStatus;
+};
+
+
+nsresult
+nsCacheService::NotifyListener(nsCacheRequest *          request,
+                               nsICacheEntryDescriptor * descriptor,
+                               nsCacheAccessMode         accessGranted,
+                               nsresult                  status)
+{
+    NS_ASSERTION(request->mThread, "no thread set in async request!");
+
+    // Swap ownership, and release listener on target thread...
+    nsICacheListener *listener = request->mListener;
+    request->mListener = nsnull;
+
+    nsCOMPtr<nsIRunnable> ev =
+            new nsCacheListenerEvent(listener, descriptor,
+                                     accessGranted, status);
+    if (!ev) {
+        // Better to leak listener and descriptor if we fail because we don't
+        // want to destroy them inside the cache service lock or on potentially
+        // the wrong thread.
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    return request->mThread->Dispatch(ev, NS_DISPATCH_NORMAL);
+}
+
+
+nsresult
+nsCacheService::ProcessRequest(nsCacheRequest *           request,
+                               PRBool                     calledFromOpenCacheEntry,
+                               nsICacheEntryDescriptor ** result)
+{
+    // !!! must be called with mLock held !!!
+    nsresult           rv;
+    nsCacheEntry *     entry = nsnull;
+    nsCacheAccessMode  accessGranted = nsICache::ACCESS_NONE;
+    if (result) *result = nsnull;
+
+    while(1) {  // Activate entry loop
+        rv = ActivateEntry(request, &entry);  // get the entry for this request
+        if (NS_FAILED(rv))  break;
+
+        while(1) { // Request Access loop
+            NS_ASSERTION(entry, "no entry in Request Access loop!");
+            // entry->RequestAccess queues request on entry
+            rv = entry->RequestAccess(request, &accessGranted);
+            if (rv != NS_ERROR_CACHE_WAIT_FOR_VALIDATION) break;
+            
+            if (request->mListener) // async exits - validate, doom, or close will resume
+                return rv;
+            
+            if (request->IsBlocking()) {
+                // XXX this is probably wrong...
+                Unlock();
+                rv = request->WaitForValidation();
+                Lock();
+            }
+
+            PR_REMOVE_AND_INIT_LINK(request);
+            if (NS_FAILED(rv)) break;   // non-blocking mode returns WAIT_FOR_VALIDATION error
+            // okay, we're ready to process this request, request access again
+        }
+        if (rv != NS_ERROR_CACHE_ENTRY_DOOMED)  break;
+
+        if (entry->IsNotInUse()) {
+            // this request was the last one keeping it around, so get rid of it
+            DeactivateEntry(entry);
+        }
+        // loop back around to look for another entry
+    }
+
+    nsICacheEntryDescriptor *descriptor = nsnull;
+    
+    if (NS_SUCCEEDED(rv))
+        rv = entry->CreateDescriptor(request, accessGranted, &descriptor);
+
+    if (request->mListener) {  // Asynchronous
+    
+        if (NS_FAILED(rv) && calledFromOpenCacheEntry)
+            return rv;  // skip notifying listener, just return rv to caller
+            
+        // call listener to report error or descriptor
+        nsresult rv2 = NotifyListener(request, descriptor, accessGranted, rv);
+        if (NS_FAILED(rv2) && NS_SUCCEEDED(rv)) {
+            rv = rv2;  // trigger delete request
+        }
+    } else {        // Synchronous
+        *result = descriptor;
+    }
+    return rv;
+}
+
+
+nsresult
+nsCacheService::OpenCacheEntry(nsCacheSession *           session,
+                               const nsACString &         key,
+                               nsCacheAccessMode          accessRequested,
+                               PRBool                     blockingMode,
+                               nsICacheListener *         listener,
+                               nsICacheEntryDescriptor ** result)
+{
+    CACHE_LOG_DEBUG(("Opening entry for session %p, key %s, mode %d, blocking %d\n",
+                     session, PromiseFlatCString(key).get(), accessRequested,
+                     blockingMode));
+    NS_ASSERTION(gService, "nsCacheService::gService is null.");
+    if (result)
+        *result = nsnull;
+
+    if (!gService->mInitialized)
+        return NS_ERROR_NOT_INITIALIZED;
+
+    nsCacheRequest * request = nsnull;
+
+    nsCacheServiceAutoLock lock;
+    nsresult rv = gService->CreateRequest(session,
+                                          key,
+                                          accessRequested,
+                                          blockingMode,
+                                          listener,
+                                          &request);
+    if (NS_FAILED(rv))  return rv;
+
+    CACHE_LOG_DEBUG(("Created request %p\n", request));
+
+    rv = gService->ProcessRequest(request, PR_TRUE, result);
+
+    // delete requests that have completed
+    if (!(listener && (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)))
+        delete request;
+
+    return rv;
+}
+
+
+nsresult
+nsCacheService::ActivateEntry(nsCacheRequest * request, 
+                              nsCacheEntry ** result)
+{
+    CACHE_LOG_DEBUG(("Activate entry for request %p\n", request));
+    
+    nsresult        rv = NS_OK;
+
+    NS_ASSERTION(request != nsnull, "ActivateEntry called with no request");
+    if (result) *result = nsnull;
+    if ((!request) || (!result))  return NS_ERROR_NULL_POINTER;
+
+    // check if the request can be satisfied
+    if (!mEnableMemoryDevice && !request->IsStreamBased())
+        return NS_ERROR_FAILURE;
+    if (!IsStorageEnabledForPolicy_Locked(request->StoragePolicy()))
+        return NS_ERROR_FAILURE;
+
+    // search active entries (including those not bound to device)
+    nsCacheEntry *entry = mActiveEntries.GetEntry(request->mKey);
+    CACHE_LOG_DEBUG(("Active entry for request %p is %p\n", request, entry));
+
+    if (!entry) {
+        // search cache devices for entry
+        PRBool collision = PR_FALSE;
+        entry = SearchCacheDevices(request->mKey, request->StoragePolicy(), &collision);
+        CACHE_LOG_DEBUG(("Device search for request %p returned %p\n",
+                         request, entry));
+        // When there is a hashkey collision just refuse to cache it...
+        if (collision) return NS_ERROR_CACHE_IN_USE;
+
+        if (entry)  entry->MarkInitialized();
+    }
+
+    if (entry) {
+        ++mCacheHits;
+        entry->Fetched();
+    } else {
+        ++mCacheMisses;
+    }
+
+    if (entry &&
+        ((request->AccessRequested() == nsICache::ACCESS_WRITE) ||
+         ((request->StoragePolicy() != nsICache::STORE_OFFLINE) &&
+          (entry->mExpirationTime <= SecondsFromPRTime(PR_Now()) &&
+           request->WillDoomEntriesIfExpired()))))
+
+    {
+        // this is FORCE-WRITE request or the entry has expired
+        rv = DoomEntry_Internal(entry);
+        if (NS_FAILED(rv)) {
+            // XXX what to do?  Increment FailedDooms counter?
+        }
+        entry = nsnull;
+    }
+
+    if (!entry) {
+        if (! (request->AccessRequested() & nsICache::ACCESS_WRITE)) {
+            // this is a READ-ONLY request
+            rv = NS_ERROR_CACHE_KEY_NOT_FOUND;
+            goto error;
+        }
+
+        entry = new nsCacheEntry(request->mKey,
+                                 request->IsStreamBased(),
+                                 request->StoragePolicy());
+        if (!entry)
+            return NS_ERROR_OUT_OF_MEMORY;
+        
+        entry->Fetched();
+        ++mTotalEntries;
+        
+        // XXX  we could perform an early bind in some cases based on storage policy
+    }
+
+    if (!entry->IsActive()) {
+        rv = mActiveEntries.AddEntry(entry);
+        if (NS_FAILED(rv)) goto error;
+        CACHE_LOG_DEBUG(("Added entry %p to mActiveEntries\n", entry));
+        entry->MarkActive();  // mark entry active, because it's now in mActiveEntries
+    }
+    *result = entry;
+    return NS_OK;
+    
+ error:
+    *result = nsnull;
+    if (entry) {
+        delete entry;
+    }
+    return rv;
+}
+
+
+nsCacheEntry *
+nsCacheService::SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, PRBool *collision)
+{
+    nsCacheEntry * entry = nsnull;
+
+    CACHE_LOG_DEBUG(("mMemoryDevice: 0x%p\n", mMemoryDevice));
+
+    *collision = PR_FALSE;
+    if ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_IN_MEMORY)) {
+        // If there is no memory device, then there is nothing to search...
+        if (mMemoryDevice) {
+            entry = mMemoryDevice->FindEntry(key, collision);
+            CACHE_LOG_DEBUG(("Searching mMemoryDevice for key %s found: 0x%p, "
+                             "collision: %d\n", key->get(), entry, collision));
+        }
+    }
+
+    if (!entry && 
+        ((policy == nsICache::STORE_ANYWHERE) || (policy == nsICache::STORE_ON_DISK))) {
+
+#ifdef NECKO_DISK_CACHE
+        if (mEnableDiskDevice) {
+            if (!mDiskDevice) {
+                nsresult rv = CreateDiskDevice();
+                if (NS_FAILED(rv))
+                    return nsnull;
+            }
+            
+            entry = mDiskDevice->FindEntry(key, collision);
+        }
+#endif // !NECKO_DISK_CACHE
+    }
+
+    if (!entry && (policy == nsICache::STORE_OFFLINE ||
+                   (policy == nsICache::STORE_ANYWHERE &&
+                    gIOService->IsOffline()))) {
+
+#ifdef NECKO_OFFLINE_CACHE
+        if (mEnableOfflineDevice) {
+            if (!mOfflineDevice) {
+                nsresult rv = CreateOfflineDevice();
+                if (NS_FAILED(rv))
+                    return nsnull;
+            }
+
+            entry = mOfflineDevice->FindEntry(key, collision);
+        }
+#endif // !NECKO_OFFLINE_CACHE
+    }
+
+    return entry;
+}
+
+
+nsCacheDevice *
+nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
+{
+    nsCacheDevice * device = entry->CacheDevice();
+    if (device)  return device;
+
+#ifdef NECKO_DISK_CACHE
+    if (entry->IsStreamData() && entry->IsAllowedOnDisk() && mEnableDiskDevice) {
+        // this is the default
+        if (!mDiskDevice) {
+            (void)CreateDiskDevice();  // ignore the error (check for mDiskDevice instead)
+        }
+
+        if (mDiskDevice) {
+            entry->MarkBinding();  // enter state of binding
+            nsresult rv = mDiskDevice->BindEntry(entry);
+            entry->ClearBinding(); // exit state of binding
+            if (NS_SUCCEEDED(rv))
+                device = mDiskDevice;
+        }
+    }
+#endif // !NECKO_DISK_CACHE
+     
+    // if we can't use mDiskDevice, try mMemoryDevice
+    if (!device && mEnableMemoryDevice && entry->IsAllowedInMemory()) {        
+        if (!mMemoryDevice) {
+            (void)CreateMemoryDevice();  // ignore the error (check for mMemoryDevice instead)
+        }
+        if (mMemoryDevice) {
+            entry->MarkBinding();  // enter state of binding
+            nsresult rv = mMemoryDevice->BindEntry(entry);
+            entry->ClearBinding(); // exit state of binding
+            if (NS_SUCCEEDED(rv))
+                device = mMemoryDevice;
+        }
+    }
+
+#ifdef NECKO_OFFLINE_CACHE
+    if (!device && entry->IsStreamData() &&
+        entry->IsAllowedOffline() && mEnableOfflineDevice) {
+        if (!mOfflineDevice) {
+            (void)CreateOfflineDevice(); // ignore the error (check for mOfflineDevice instead)
+        }
+
+        if (mOfflineDevice) {
+            entry->MarkBinding();
+            nsresult rv = mOfflineDevice->BindEntry(entry);
+            entry->ClearBinding();
+            if (NS_SUCCEEDED(rv))
+                device = mOfflineDevice;
+        }
+    }
+#endif // ! NECKO_OFFLINE_CACHE
+
+    if (device) 
+        entry->SetCacheDevice(device);
+    return device;
+}
+
+
+nsresult
+nsCacheService::DoomEntry(nsCacheEntry * entry)
+{
+    return gService->DoomEntry_Internal(entry);
+}
+
+
+nsresult
+nsCacheService::DoomEntry_Internal(nsCacheEntry * entry)
+{
+    if (entry->IsDoomed())  return NS_OK;
+    
+    CACHE_LOG_DEBUG(("Dooming entry %p\n", entry));
+    nsresult  rv = NS_OK;
+    entry->MarkDoomed();
+    
+    NS_ASSERTION(!entry->IsBinding(), "Dooming entry while binding device.");
+    nsCacheDevice * device = entry->CacheDevice();
+    if (device)  device->DoomEntry(entry);
+
+    if (entry->IsActive()) {
+        // remove from active entries
+        mActiveEntries.RemoveEntry(entry);
+        CACHE_LOG_DEBUG(("Removed entry %p from mActiveEntries\n", entry));
+        entry->MarkInactive();
+     }
+
+    // put on doom list to wait for descriptors to close
+    NS_ASSERTION(PR_CLIST_IS_EMPTY(entry), "doomed entry still on device list");
+    PR_APPEND_LINK(entry, &mDoomedEntries);
+
+    // tell pending requests to get on with their lives...
+    rv = ProcessPendingRequests(entry);
+    
+    // All requests have been removed, but there may still be open descriptors
+    if (entry->IsNotInUse()) {
+        DeactivateEntry(entry); // tell device to get rid of it
+    }
+    return rv;
+}
+
+
+void
+nsCacheService::OnProfileShutdown(PRBool cleanse)
+{
+    if (!gService)  return;
+    if (!gService->mInitialized) {
+        // The cache service has been shut down, but someone is still holding
+        // a reference to it. Ignore this call.
+        return;
+    }
+    nsCacheServiceAutoLock lock;
+
+    gService->DoomActiveEntries();
+    gService->ClearDoomList();
+
+#ifdef NECKO_DISK_CACHE
+    if (gService->mDiskDevice && gService->mEnableDiskDevice) {
+        if (cleanse)
+            gService->mDiskDevice->EvictEntries(nsnull);
+
+        gService->mDiskDevice->Shutdown();
+    }
+    gService->mEnableDiskDevice = PR_FALSE;
+#endif // !NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+    if (gService->mOfflineDevice && gService->mEnableOfflineDevice) {
+        if (cleanse)
+            gService->mOfflineDevice->EvictEntries(nsnull);
+
+        gService->mOfflineDevice->Shutdown();
+    }
+    gService->mEnableOfflineDevice = PR_FALSE;
+#endif // !NECKO_OFFLINE_CACHE
+
+    if (gService->mMemoryDevice) {
+        // clear memory cache
+        gService->mMemoryDevice->EvictEntries(nsnull);
+    }
+
+}
+
+
+void
+nsCacheService::OnProfileChanged()
+{
+    if (!gService)  return;
+
+    CACHE_LOG_DEBUG(("nsCacheService::OnProfileChanged"));
+ 
+    nsCacheServiceAutoLock lock;
+    
+    gService->mEnableDiskDevice    = gService->mObserver->DiskCacheEnabled();
+    gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
+    gService->mEnableMemoryDevice  = gService->mObserver->MemoryCacheEnabled();
+
+#ifdef NECKO_DISK_CACHE
+    if (gService->mDiskDevice) {
+        gService->mDiskDevice->SetCacheParentDirectory(gService->mObserver->DiskCacheParentDirectory());
+        gService->mDiskDevice->SetCapacity(gService->mObserver->DiskCacheCapacity());
+
+        // XXX initialization of mDiskDevice could be made lazily, if mEnableDiskDevice is false
+        nsresult rv = gService->mDiskDevice->Init();
+        if (NS_FAILED(rv)) {
+            NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing disk device failed");
+            gService->mEnableDiskDevice = PR_FALSE;
+            // XXX delete mDiskDevice?
+        }
+    }
+#endif // !NECKO_DISK_CACHE
+
+#ifdef NECKO_OFFLINE_CACHE
+    if (gService->mOfflineDevice) {
+        gService->mOfflineDevice->SetCacheParentDirectory(gService->mObserver->OfflineCacheParentDirectory());
+        gService->mOfflineDevice->SetCapacity(gService->mObserver->OfflineCacheCapacity());
+
+        // XXX initialization of mOfflineDevice could be made lazily, if mEnableOfflineDevice is false
+        nsresult rv = gService->mOfflineDevice->Init();
+        if (NS_FAILED(rv)) {
+            NS_ERROR("nsCacheService::OnProfileChanged: Re-initializing offline device failed");
+            gService->mEnableOfflineDevice = PR_FALSE;
+            // XXX delete mOfflineDevice?
+        }
+    }
+#endif // !NECKO_OFFLINE_CACHE
+    
+    // If memoryDevice exists, reset its size to the new profile
+    if (gService->mMemoryDevice) {
+        if (gService->mEnableMemoryDevice) {
+            // make sure that capacity is reset to the right value
+            PRInt32 capacity = gService->mObserver->MemoryCacheCapacity();
+            CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
+                             capacity));
+            gService->mMemoryDevice->SetCapacity(capacity);
+        } else {
+            // tell memory device to evict everything
+            CACHE_LOG_DEBUG(("memory device disabled\n"));
+            gService->mMemoryDevice->SetCapacity(0);
+            // Don't delete memory device, because some entries may be active still...
+        }
+    }
+}
+
+
+void
+nsCacheService::SetDiskCacheEnabled(PRBool  enabled)
+{
+    if (!gService)  return;
+    nsCacheServiceAutoLock lock;
+    gService->mEnableDiskDevice = enabled;
+}
+
+
+void
+nsCacheService::SetDiskCacheCapacity(PRInt32  capacity)
+{
+    if (!gService)  return;
+    nsCacheServiceAutoLock lock;
+
+#ifdef NECKO_DISK_CACHE
+    if (gService->mDiskDevice) {
+        gService->mDiskDevice->SetCapacity(capacity);
+    }
+#endif // !NECKO_DISK_CACHE
+    
+    gService->mEnableDiskDevice = gService->mObserver->DiskCacheEnabled();
+}
+
+void
+nsCacheService::SetOfflineCacheEnabled(PRBool  enabled)
+{
+    if (!gService)  return;
+    nsCacheServiceAutoLock lock;
+    gService->mEnableOfflineDevice = enabled;
+}
+
+void
+nsCacheService::SetOfflineCacheCapacity(PRInt32  capacity)
+{
+    if (!gService)  return;
+    nsCacheServiceAutoLock lock;
+
+#ifdef NECKO_OFFLINE_CACHE
+    if (gService->mOfflineDevice) {
+        gService->mOfflineDevice->SetCapacity(capacity);
+    }
+#endif // !NECKO_OFFLINE_CACHE
+
+    gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
+}
+
+
+void
+nsCacheService::SetMemoryCache()
+{
+    if (!gService)  return;
+
+    CACHE_LOG_DEBUG(("nsCacheService::SetMemoryCache"));
+
+    nsCacheServiceAutoLock lock;
+
+    gService->mEnableMemoryDevice = gService->mObserver->MemoryCacheEnabled();
+
+    if (gService->mEnableMemoryDevice) {
+        if (gService->mMemoryDevice) {
+            PRInt32 capacity = gService->mObserver->MemoryCacheCapacity();
+            // make sure that capacity is reset to the right value
+            CACHE_LOG_DEBUG(("Resetting memory device capacity to %d\n",
+                             capacity));
+            gService->mMemoryDevice->SetCapacity(capacity);
+        }
+    } else {
+        if (gService->mMemoryDevice) {
+            // tell memory device to evict everything
+            CACHE_LOG_DEBUG(("memory device disabled\n"));
+            gService->mMemoryDevice->SetCapacity(0);
+            // Don't delete memory device, because some entries may be active still...
+        }
+    }
+}
+
+
+/******************************************************************************
+ * static methods for nsCacheEntryDescriptor
+ *****************************************************************************/
+#ifdef XP_MAC
+#pragma mark -
+#endif
+
+void
+nsCacheService::CloseDescriptor(nsCacheEntryDescriptor * descriptor)
+{
+    // ask entry to remove descriptor
+    nsCacheEntry * entry       = descriptor->CacheEntry();
+    PRBool         stillActive = entry->RemoveDescriptor(descriptor);
+    nsresult       rv          = NS_OK;
+
+    if (!entry->IsValid()) {
+        rv = gService->ProcessPendingRequests(entry);
+    }
+
+    if (!stillActive) {
+        gService->DeactivateEntry(entry);
+    }
+}
+
+
+nsresult        
+nsCacheService::GetFileForEntry(nsCacheEntry *         entry,
+                                nsIFile **             result)
+{
+    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
+    if (!device)  return  NS_ERROR_UNEXPECTED;
+    
+    return device->GetFileForEntry(entry, result);
+}
+
+
+nsresult
+nsCacheService::OpenInputStreamForEntry(nsCacheEntry *     entry,
+                                        nsCacheAccessMode  mode,
+                                        PRUint32           offset,
+                                        nsIInputStream  ** result)
+{
+    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
+    if (!device)  return  NS_ERROR_UNEXPECTED;
+
+    return device->OpenInputStreamForEntry(entry, mode, offset, result);
+}
+
+nsresult
+nsCacheService::OpenOutputStreamForEntry(nsCacheEntry *     entry,
+                                         nsCacheAccessMode  mode,
+                                         PRUint32           offset,
+                                         nsIOutputStream ** result)
+{
+    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
+    if (!device)  return  NS_ERROR_UNEXPECTED;
+
+    return device->OpenOutputStreamForEntry(entry, mode, offset, result);
+}
+
+
+nsresult
+nsCacheService::OnDataSizeChange(nsCacheEntry * entry, PRInt32 deltaSize)
+{
+    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
+    if (!device)  return  NS_ERROR_UNEXPECTED;
+
+    return device->OnDataSizeChange(entry, deltaSize);
+}
+
+void
+nsCacheService::Lock()
+{
+    PR_Lock(gService->mLock);
+
+#if defined(DEBUG)
+    gService->mLockedThread = PR_GetCurrentThread();
+#endif
+}
+
+void
+nsCacheService::Unlock()
+{
+    NS_ASSERTION(gService->mLockedThread == PR_GetCurrentThread(), "oops");
+
+    nsTArray<nsISupports*> doomed;
+    doomed.SwapElements(gService->mDoomedObjects);
+
+#if defined(DEBUG)
+    gService->mLockedThread = nsnull;
+#endif
+    PR_Unlock(gService->mLock);
+
+    for (PRUint32 i = 0; i < doomed.Length(); ++i)
+        doomed[i]->Release();
+}
+
+void
+nsCacheService::ReleaseObject_Locked(nsISupports * obj,
+                                     nsIEventTarget * target)
+{
+    NS_ASSERTION(gService->mLockedThread == PR_GetCurrentThread(), "oops");
+
+    PRBool isCur;
+    if (!target || NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur) {
+        gService->mDoomedObjects.AppendElement(obj);
+    } else {
+        NS_ProxyRelease(target, obj);
+    }
+}
+
+
+nsresult
+nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element)
+{
+    entry->SetData(element);
+    entry->TouchData();
+    return NS_OK;
+}
+
+
+nsresult
+nsCacheService::ValidateEntry(nsCacheEntry * entry)
+{
+    nsCacheDevice * device = gService->EnsureEntryHasDevice(entry);
+    if (!device)  return  NS_ERROR_UNEXPECTED;
+
+    entry->MarkValid();
+    nsresult rv = gService->ProcessPendingRequests(entry);
+    NS_ASSERTION(rv == NS_OK, "ProcessPendingRequests failed.");
+    // XXX what else should be done?
+
+    return rv;
+}
+
+#ifdef XP_MAC
+#pragma mark -
+#endif
+
+
+void
+nsCacheService::DeactivateEntry(nsCacheEntry * entry)
+{
+    CACHE_LOG_DEBUG(("Deactivating entry %p\n", entry));
+    nsresult  rv = NS_OK;
+    NS_ASSERTION(entry->IsNotInUse(), "### deactivating an entry while in use!");
+    nsCacheDevice * device = nsnull;
+
+    if (mMaxDataSize < entry->DataSize() )     mMaxDataSize = entry->DataSize();
+    if (mMaxMetaSize < entry->MetaDataSize() ) mMaxMetaSize = entry->MetaDataSize();
+
+    if (entry->IsDoomed()) {
+        // remove from Doomed list
+        PR_REMOVE_AND_INIT_LINK(entry);
+    } else if (entry->IsActive()) {
+        // remove from active entries
+        mActiveEntries.RemoveEntry(entry);
+        CACHE_LOG_DEBUG(("Removed deactivated entry %p from mActiveEntries\n",
+                         entry));
+        entry->MarkInactive();
+
+        // bind entry if necessary to store meta-data
+        device = EnsureEntryHasDevice(entry); 
+        if (!device) {
+            CACHE_LOG_DEBUG(("DeactivateEntry: unable to bind active "
+                             "entry %p\n",
+                             entry));
+            NS_WARNING("DeactivateEntry: unable to bind active entry\n");
+            return;
+        }
+    } else {
+        // if mInitialized == PR_FALSE,
+        // then we're shutting down and this state is okay.
+        NS_ASSERTION(!mInitialized, "DeactivateEntry: bad cache entry state.");
+    }
+
+    device = entry->CacheDevice();
+    if (device) {
+        rv = device->DeactivateEntry(entry);
+        if (NS_FAILED(rv)) {
+            // increment deactivate failure count
+            ++mDeactivateFailures;
+        }
+    } else {
+        // increment deactivating unbound entry statistic
+        ++mDeactivatedUnboundEntries;
+        delete entry; // because no one else will
+    }
+}
+
+
+nsresult
+nsCacheService::ProcessPendingRequests(nsCacheEntry * entry)
+{
+    nsresult            rv = NS_OK;
+    nsCacheRequest *    request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
+    nsCacheRequest *    nextRequest;
+    PRBool              newWriter = PR_FALSE;
+    
+    if (request == &entry->mRequestQ)  return NS_OK;    // no queued requests
+
+    if (!entry->IsDoomed() && entry->IsInvalid()) {
+        // 1st descriptor closed w/o MarkValid()
+        NS_ASSERTION(PR_CLIST_IS_EMPTY(&entry->mDescriptorQ), "shouldn't be here with open descriptors");
+
+#if DEBUG
+        // verify no ACCESS_WRITE requests(shouldn't have any of these)
+        while (request != &entry->mRequestQ) {
+            NS_ASSERTION(request->AccessRequested() != nsICache::ACCESS_WRITE,
+                         "ACCESS_WRITE request should have been given a new entry");
+            request = (nsCacheRequest *)PR_NEXT_LINK(request);
+        }
+        request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);        
+#endif
+        // find first request with ACCESS_READ_WRITE (if any) and promote it to 1st writer
+        while (request != &entry->mRequestQ) {
+            if (request->AccessRequested() == nsICache::ACCESS_READ_WRITE) {
+                newWriter = PR_TRUE;
+                break;
+            }
+
+            request = (nsCacheRequest *)PR_NEXT_LINK(request);
+        }
+        
+        if (request == &entry->mRequestQ)   // no requests asked for ACCESS_READ_WRITE, back to top
+            request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
+        
+        // XXX what should we do if there are only READ requests in queue?
+        // XXX serialize their accesses, give them only read access, but force them to check validate flag?
+        // XXX or do readers simply presume the entry is valid
+    }
+
+    nsCacheAccessMode  accessGranted = nsICache::ACCESS_NONE;
+
+    while (request != &entry->mRequestQ) {
+        nextRequest = (nsCacheRequest *)PR_NEXT_LINK(request);
+
+        if (request->mListener) {
+
+            // Async request
+            PR_REMOVE_AND_INIT_LINK(request);
+
+            if (entry->IsDoomed()) {
+                rv = ProcessRequest(request, PR_FALSE, nsnull);
+                if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION)
+                    rv = NS_OK;
+                else
+                    delete request;
+
+                if (NS_FAILED(rv)) {
+                    // XXX what to do?
+                }
+            } else if (entry->IsValid() || newWriter) {
+                rv = entry->RequestAccess(request, &accessGranted);
+                NS_ASSERTION(NS_SUCCEEDED(rv),
+                             "if entry is valid, RequestAccess must succeed.");
+                // XXX if (newWriter)  NS_ASSERTION( accessGranted == request->AccessRequested(), "why not?");
+
+                // entry->CreateDescriptor dequeues request, and queues descriptor
+                nsICacheEntryDescriptor *descriptor = nsnull;
+                rv = entry->CreateDescriptor(request,
+                                             accessGranted,
+                                             &descriptor);
+                
+                // post call to listener to report error or descriptor
+                rv = NotifyListener(request, descriptor, accessGranted, rv);
+                delete request;
+                if (NS_FAILED(rv)) {
+                    // XXX what to do?
+                }
+                
+            } else {
+                // XXX bad state
+            }
+        } else {
+
+            // Synchronous request
+            request->WakeUp();
+        }
+        if (newWriter)  break;  // process remaining requests after validation
+        request = nextRequest;
+    }
+
+    return NS_OK;
+}
+
+
+void
+nsCacheService::ClearPendingRequests(nsCacheEntry * entry)
+{
+    nsCacheRequest * request = (nsCacheRequest *)PR_LIST_HEAD(&entry->mRequestQ);
+    
+    while (request != &entry->mRequestQ) {
+        nsCacheRequest * next = (nsCacheRequest *)PR_NEXT_LINK(request);
+
+        // XXX we're just dropping these on the floor for now...definitely wrong.
+        PR_REMOVE_AND_INIT_LINK(request);
+        delete request;
+        request = next;
+    }
+}
+
+
+void
+nsCacheService::ClearDoomList()
+{
+    nsCacheEntry * entry = (nsCacheEntry *)PR_LIST_HEAD(&mDoomedEntries);
+
+    while (entry != &mDoomedEntries) {
+        nsCacheEntry * next = (nsCacheEntry *)PR_NEXT_LINK(entry);
+        
+         entry->DetachDescriptors();
+         DeactivateEntry(entry);
+         entry = next;
+    }        
+}
+
+
+void
+nsCacheService::ClearActiveEntries()
+{
+    mActiveEntries.VisitEntries(DeactivateAndClearEntry, nsnull);
+    mActiveEntries.Shutdown();
+}
+
+
+PLDHashOperator
+nsCacheService::DeactivateAndClearEntry(PLDHashTable *    table,
+                                        PLDHashEntryHdr * hdr,
+                                        PRUint32          number,
+                                        void *            arg)
+{
+    nsCacheEntry * entry = ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry;
+    NS_ASSERTION(entry, "### active entry = nsnull!");
+    gService->ClearPendingRequests(entry);
+    entry->DetachDescriptors();
+    
+    entry->MarkInactive();  // so we don't call Remove() while we're enumerating
+    gService->DeactivateEntry(entry);
+    
+    return PL_DHASH_REMOVE; // and continue enumerating
+}
+
+
+void
+nsCacheService::DoomActiveEntries()
+{
+    nsAutoTArray<nsCacheEntry*, 8> array;
+
+    mActiveEntries.VisitEntries(RemoveActiveEntry, &array);
+
+    PRUint32 count = array.Length();
+    for (PRUint32 i=0; i < count; ++i)
+        DoomEntry_Internal(array[i]);
+}
+
+
+PLDHashOperator
+nsCacheService::RemoveActiveEntry(PLDHashTable *    table,
+                                  PLDHashEntryHdr * hdr,
+                                  PRUint32          number,
+                                  void *            arg)
+{
+    nsCacheEntry * entry = ((nsCacheEntryHashTableEntry *)hdr)->cacheEntry;
+    NS_ASSERTION(entry, "### active entry = nsnull!");
+
+    nsTArray<nsCacheEntry*> * array = (nsTArray<nsCacheEntry*> *) arg;
+    NS_ASSERTION(array, "### array = nsnull!");
+    array->AppendElement(entry);
+
+    // entry is being removed from the active entry list
+    entry->MarkInactive();
+    return PL_DHASH_REMOVE; // and continue enumerating
+}
+
+
+#if defined(PR_LOGGING)
+void
+nsCacheService::LogCacheStatistics()
+{
+    PRUint32 hitPercentage = (PRUint32)((((double)mCacheHits) /
+        ((double)(mCacheHits + mCacheMisses))) * 100);
+    CACHE_LOG_ALWAYS(("\nCache Service Statistics:\n\n"));
+    CACHE_LOG_ALWAYS(("    TotalEntries   = %d\n", mTotalEntries));
+    CACHE_LOG_ALWAYS(("    Cache Hits     = %d\n", mCacheHits));
+    CACHE_LOG_ALWAYS(("    Cache Misses   = %d\n", mCacheMisses));
+    CACHE_LOG_ALWAYS(("    Cache Hit %%    = %d%%\n", hitPercentage));
+    CACHE_LOG_ALWAYS(("    Max Key Length = %d\n", mMaxKeyLength));
+    CACHE_LOG_ALWAYS(("    Max Meta Size  = %d\n", mMaxMetaSize));
+    CACHE_LOG_ALWAYS(("    Max Data Size  = %d\n", mMaxDataSize));
+    CACHE_LOG_ALWAYS(("\n"));
+    CACHE_LOG_ALWAYS(("    Deactivate Failures         = %d\n",
+                      mDeactivateFailures));
+    CACHE_LOG_ALWAYS(("    Deactivated Unbound Entries = %d\n",
+                      mDeactivatedUnboundEntries));
+}
+#endif
+
+
+void
+nsCacheService::OnEnterExitPrivateBrowsing()
+{
+    if (!gService)  return;
+    nsCacheServiceAutoLock lock;
+
+    gService->DoomActiveEntries();
+
+    if (gService->mMemoryDevice) {
+        // clear memory cache
+        gService->mMemoryDevice->EvictEntries(nsnull);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheService.h
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheService.h, released
+ * February 10, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan  <gordon@netscape.com>
+ *   Patrick C. Beard <beard@netscape.com>
+ *   Darin Fisher     <darin@netscape.com>
+ *   Ehsan Akhgari    <ehsan.akhgari@gmail.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 ***** */
+
+
+#ifndef _nsCacheService_h_
+#define _nsCacheService_h_
+
+#include "nsICacheService.h"
+#include "nsCacheSession.h"
+#include "nsCacheDevice.h"
+#include "nsCacheEntry.h"
+
+#include "prlock.h"
+#include "prthread.h"
+#include "nsIObserver.h"
+#include "nsString.h"
+#include "nsProxiedService.h"
+#include "nsTArray.h"
+
+class nsCacheRequest;
+class nsCacheProfilePrefObserver;
+class nsDiskCacheDevice;
+class nsMemoryCacheDevice;
+class nsOfflineCacheDevice;
+class nsCacheServiceAutoLock;
+
+
+/******************************************************************************
+ *  nsCacheService
+ ******************************************************************************/
+
+class nsCacheService : public nsICacheService
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICACHESERVICE
+    
+    nsCacheService();
+    virtual ~nsCacheService();
+
+    // Define a Create method to be used with a factory:
+    static NS_METHOD
+    Create(nsISupports* outer, const nsIID& iid, void* *result);
+
+
+    /**
+     * Methods called by nsCacheSession
+     */
+    static nsresult  OpenCacheEntry(nsCacheSession *           session,
+                                    const nsACString &         key,
+                                    nsCacheAccessMode          accessRequested,
+                                    PRBool                     blockingMode,
+                                    nsICacheListener *         listener,
+                                    nsICacheEntryDescriptor ** result);
+
+    static nsresult  EvictEntriesForSession(nsCacheSession *   session);
+
+    static nsresult  IsStorageEnabledForPolicy(nsCacheStoragePolicy  storagePolicy,
+                                               PRBool *              result);
+
+    /**
+     * Methods called by nsCacheEntryDescriptor
+     */
+
+    static void      CloseDescriptor(nsCacheEntryDescriptor * descriptor);
+
+    static nsresult  GetFileForEntry(nsCacheEntry *         entry,
+                                     nsIFile **             result);
+
+    static nsresult  OpenInputStreamForEntry(nsCacheEntry *     entry,
+                                             nsCacheAccessMode  mode,
+                                             PRUint32           offset,
+                                             nsIInputStream **  result);
+
+    static nsresult  OpenOutputStreamForEntry(nsCacheEntry *     entry,
+                                              nsCacheAccessMode  mode,
+                                              PRUint32           offset,
+                                              nsIOutputStream ** result);
+
+    static nsresult  OnDataSizeChange(nsCacheEntry * entry, PRInt32 deltaSize);
+
+    static nsresult  SetCacheElement(nsCacheEntry * entry, nsISupports * element);
+
+    static nsresult  ValidateEntry(nsCacheEntry * entry);
+
+
+    /**
+     * Methods called by any cache classes
+     */
+
+    static
+    nsCacheService * GlobalInstance()   { return gService; }
+    
+    static nsresult  DoomEntry(nsCacheEntry * entry);
+
+    static PRBool    IsStorageEnabledForPolicy_Locked(nsCacheStoragePolicy policy);
+
+    // This method may be called to release an object while the cache service
+    // lock is being held.  If a non-null target is specified and the target
+    // does not correspond to the current thread, then the release will be
+    // proxied to the specified target.  Otherwise, the object will be added to
+    // the list of objects to be released when the cache service is unlocked.
+    static void      ReleaseObject_Locked(nsISupports *    object,
+                                          nsIEventTarget * target = nsnull);
+
+    /**
+     * Methods called by nsCacheProfilePrefObserver
+     */
+    static void      OnProfileShutdown(PRBool cleanse);
+    static void      OnProfileChanged();
+
+    static void      SetDiskCacheEnabled(PRBool  enabled);
+    // Sets the disk cache capacity (in kilobytes)
+    static void      SetDiskCacheCapacity(PRInt32  capacity);
+
+    static void      SetOfflineCacheEnabled(PRBool  enabled);
+    // Sets the offline cache capacity (in kilobytes)
+    static void      SetOfflineCacheCapacity(PRInt32  capacity);
+
+    static void      SetMemoryCache();
+
+    static void      OnEnterExitPrivateBrowsing();
+
+    nsresult         Init();
+    void             Shutdown();
+private:
+    friend class nsCacheServiceAutoLock;
+    friend class nsOfflineCacheDevice;
+
+    /**
+     * Internal Methods
+     */
+
+    static void      Lock();
+    static void      Unlock();
+
+    nsresult         CreateDiskDevice();
+    nsresult         CreateOfflineDevice();
+    nsresult         CreateMemoryDevice();
+
+    nsresult         CreateRequest(nsCacheSession *   session,
+                                   const nsACString & clientKey,
+                                   nsCacheAccessMode  accessRequested,
+                                   PRBool             blockingMode,
+                                   nsICacheListener * listener,
+                                   nsCacheRequest **  request);
+
+    nsresult         DoomEntry_Internal(nsCacheEntry * entry);
+
+    nsresult         EvictEntriesForClient(const char *          clientID,
+                                           nsCacheStoragePolicy  storagePolicy);
+
+    // Notifies request listener asynchronously on the request's thread, and
+    // releases the descriptor on the request's thread.  If this method fails,
+    // the descriptor is not released.
+    nsresult         NotifyListener(nsCacheRequest *          request,
+                                    nsICacheEntryDescriptor * descriptor,
+                                    nsCacheAccessMode         accessGranted,
+                                    nsresult                  error);
+
+    nsresult         ActivateEntry(nsCacheRequest * request, nsCacheEntry ** entry);
+
+    nsCacheDevice *  EnsureEntryHasDevice(nsCacheEntry * entry);
+
+    nsCacheEntry *   SearchCacheDevices(nsCString * key, nsCacheStoragePolicy policy, PRBool *collision);
+
+    void             DeactivateEntry(nsCacheEntry * entry);
+
+    nsresult         ProcessRequest(nsCacheRequest *           request,
+                                    PRBool                     calledFromOpenCacheEntry,
+                                    nsICacheEntryDescriptor ** result);
+
+    nsresult         ProcessPendingRequests(nsCacheEntry * entry);
+
+    void             ClearPendingRequests(nsCacheEntry * entry);
+    void             ClearDoomList(void);
+    void             ClearActiveEntries(void);
+    void             DoomActiveEntries(void);
+
+    static
+    PLDHashOperator  DeactivateAndClearEntry(PLDHashTable *    table,
+                                             PLDHashEntryHdr * hdr,
+                                             PRUint32          number,
+                                             void *            arg);
+    static
+    PLDHashOperator  RemoveActiveEntry(PLDHashTable *    table,
+                                       PLDHashEntryHdr * hdr,
+                                       PRUint32          number,
+                                       void *            arg);
+#if defined(PR_LOGGING)
+    void LogCacheStatistics();
+#endif
+
+    /**
+     *  Data Members
+     */
+
+    static nsCacheService *         gService;  // there can be only one...
+    
+    nsCacheProfilePrefObserver *    mObserver;
+    
+    PRLock *                        mLock;
+
+#if defined(DEBUG)
+    PRThread *                      mLockedThread;  // The thread holding mLock
+#endif
+
+    nsTArray<nsISupports*>          mDoomedObjects;
+    
+    PRBool                          mInitialized;
+    
+    PRBool                          mEnableMemoryDevice;
+    PRBool                          mEnableDiskDevice;
+    PRBool                          mEnableOfflineDevice;
+
+    nsMemoryCacheDevice *           mMemoryDevice;
+    nsDiskCacheDevice *             mDiskDevice;
+    nsOfflineCacheDevice *          mOfflineDevice;
+
+    nsCacheEntryHashTable           mActiveEntries;
+    PRCList                         mDoomedEntries;
+
+    // stats
+    
+    PRUint32                        mTotalEntries;
+    PRUint32                        mCacheHits;
+    PRUint32                        mCacheMisses;
+    PRUint32                        mMaxKeyLength;
+    PRUint32                        mMaxDataSize;
+    PRUint32                        mMaxMetaSize;
+
+    // Unexpected error totals
+    PRUint32                        mDeactivateFailures;
+    PRUint32                        mDeactivatedUnboundEntries;
+};
+
+/******************************************************************************
+ *  nsCacheServiceAutoLock
+ ******************************************************************************/
+
+// Instantiate this class to acquire the cache service lock for a particular
+// execution scope.
+class nsCacheServiceAutoLock {
+public:
+    nsCacheServiceAutoLock() {
+        nsCacheService::Lock();
+    }
+    ~nsCacheServiceAutoLock() {
+        nsCacheService::Unlock();
+    }
+};
+
+#endif // _nsCacheService_h_
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheSession.cpp
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheSession.h, released
+ * February 23, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan <gordon@netscape.com>
+ *   Patrick Beard   <beard@netscape.com>
+ *   Darin Fisher    <darin@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 "nsCacheSession.h"
+#include "nsCacheService.h"
+#include "nsCRT.h"
+
+NS_IMPL_ISUPPORTS1(nsCacheSession, nsICacheSession)
+
+nsCacheSession::nsCacheSession(const char *         clientID,
+                               nsCacheStoragePolicy storagePolicy,
+                               PRBool               streamBased)
+    : mClientID(clientID),
+      mInfo(0)
+{
+  SetStoragePolicy(storagePolicy);
+
+  if (streamBased) MarkStreamBased();
+  else SetStoragePolicy(nsICache::STORE_IN_MEMORY);
+
+  MarkDoomEntriesIfExpired();
+}
+
+nsCacheSession::~nsCacheSession()
+{
+  /* destructor code */
+    // notify service we are going away?
+}
+
+
+NS_IMETHODIMP nsCacheSession::GetDoomEntriesIfExpired(PRBool *result)
+{
+    NS_ENSURE_ARG_POINTER(result);
+    *result = WillDoomEntriesIfExpired();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP nsCacheSession::SetDoomEntriesIfExpired(PRBool doomEntriesIfExpired)
+{
+    if (doomEntriesIfExpired)  MarkDoomEntriesIfExpired();
+    else                       ClearDoomEntriesIfExpired();
+    return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsCacheSession::OpenCacheEntry(const nsACString &         key, 
+                               nsCacheAccessMode          accessRequested,
+                               PRBool                     blockingMode,
+                               nsICacheEntryDescriptor ** result)
+{
+    nsresult rv;
+    rv =  nsCacheService::OpenCacheEntry(this,
+                                         key,
+                                         accessRequested,
+                                         blockingMode,
+                                         nsnull, // no listener
+                                         result);
+    return rv;
+}
+
+
+NS_IMETHODIMP nsCacheSession::AsyncOpenCacheEntry(const nsACString & key,
+                                                  nsCacheAccessMode accessRequested,
+                                                  nsICacheListener *listener)
+{
+    nsresult rv;
+    rv = nsCacheService::OpenCacheEntry(this,
+                                        key,
+                                        accessRequested,
+                                        nsICache::BLOCKING,
+                                        listener,
+                                        nsnull); // no result
+
+    if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) rv = NS_OK;
+    return rv;
+}
+
+NS_IMETHODIMP nsCacheSession::EvictEntries()
+{
+    return nsCacheService::EvictEntriesForSession(this);
+}
+
+
+NS_IMETHODIMP nsCacheSession::IsStorageEnabled(PRBool *result)
+{
+
+    return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result);
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsCacheSession.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheSession.h, released
+ * February 23, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan <gordon@netscape.com>
+ *   Patrick Beard   <beard@netscape.com>
+ *   Darin Fisher    <darin@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 ***** */
+
+#ifndef _nsCacheSession_h_
+#define _nsCacheSession_h_
+
+#include "nspr.h"
+#include "nsError.h"
+#include "nsICacheSession.h"
+#include "nsString.h"
+
+class nsCacheSession : public nsICacheSession
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICACHESESSION
+    
+    nsCacheSession(const char * clientID, nsCacheStoragePolicy storagePolicy, PRBool streamBased);
+    virtual ~nsCacheSession();
+    
+    nsCString *           ClientID()      { return &mClientID; }
+
+    enum SessionInfo {
+        eStoragePolicyMask        = 0x000000FF,
+        eStreamBasedMask          = 0x00000100,
+        eDoomEntriesIfExpiredMask = 0x00001000
+    };
+
+    void   MarkStreamBased()  { mInfo |=  eStreamBasedMask; }
+    void   ClearStreamBased() { mInfo &= ~eStreamBasedMask; }
+    PRBool IsStreamBased()    { return (mInfo & eStreamBasedMask) != 0; }
+
+    void   MarkDoomEntriesIfExpired()  { mInfo |=  eDoomEntriesIfExpiredMask; }
+    void   ClearDoomEntriesIfExpired() { mInfo &= ~eDoomEntriesIfExpiredMask; }
+    PRBool WillDoomEntriesIfExpired()  { return (0 != (mInfo & eDoomEntriesIfExpiredMask)); }
+
+    nsCacheStoragePolicy  StoragePolicy()
+    {
+        return (nsCacheStoragePolicy)(mInfo & eStoragePolicyMask);
+    }
+
+    void SetStoragePolicy(nsCacheStoragePolicy policy)
+    {
+        NS_ASSERTION(policy <= 0xFF, "too many bits in nsCacheStoragePolicy");
+        mInfo &= ~eStoragePolicyMask; // clear storage policy bits
+        mInfo |= policy;
+    }
+
+private:
+    nsCString               mClientID;
+    PRUint32                mInfo;
+};
+
+#endif // _nsCacheSession_h_
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsDeleteDir.cpp
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** 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 Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Darin Fisher <darin@meer.net>
+ *
+ * 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 "nsDeleteDir.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "prthread.h"
+
+static void DeleteDirThreadFunc(void *arg)
+{
+  nsIFile *dir = static_cast<nsIFile *>(arg);
+  dir->Remove(PR_TRUE);
+  NS_RELEASE(dir);
+}
+
+nsresult DeleteDir(nsIFile *dirIn, PRBool moveToTrash, PRBool sync)
+{
+  nsresult rv;
+  nsCOMPtr<nsIFile> trash, dir;
+
+  // Need to make a clone of this since we don't want to modify the input
+  // file object.
+  rv = dirIn->Clone(getter_AddRefs(dir));
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (moveToTrash)
+  {
+    rv = GetTrashDir(dir, &trash);
+    if (NS_FAILED(rv))
+      return rv;
+
+    nsCOMPtr<nsIFile> subDir;
+    rv = trash->Clone(getter_AddRefs(subDir));
+    if (NS_FAILED(rv))
+      return rv;
+
+    rv = subDir->AppendNative(NS_LITERAL_CSTRING("Trash"));
+    if (NS_FAILED(rv))
+      return rv;
+
+    rv = subDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
+    if (NS_FAILED(rv))
+      return rv;
+
+    rv = dir->MoveToNative(subDir, EmptyCString());
+    if (NS_FAILED(rv))
+      return rv;
+  }
+  else
+  {
+    // we want to pass a clone of the original off to the worker thread.
+    trash.swap(dir);
+  }
+
+  // Steal ownership of trash directory; let the thread release it.
+  nsIFile *trashRef = nsnull;
+  trash.swap(trashRef);
+
+  if (sync)
+  {
+    DeleteDirThreadFunc(trashRef);
+  }
+  else
+  {
+    // now, invoke the worker thread
+    PRThread *thread = PR_CreateThread(PR_USER_THREAD,
+                                       DeleteDirThreadFunc,
+                                       trashRef,
+                                       PR_PRIORITY_LOW,
+                                       PR_GLOBAL_THREAD,
+                                       PR_UNJOINABLE_THREAD,
+                                       0);
+    if (!thread)
+      return NS_ERROR_UNEXPECTED;
+  }
+
+  return NS_OK;
+}
+
+nsresult GetTrashDir(nsIFile *target, nsCOMPtr<nsIFile> *result)
+{
+  nsresult rv = target->Clone(getter_AddRefs(*result));
+  if (NS_FAILED(rv))
+    return rv;
+
+  nsCAutoString leaf;
+  rv = (*result)->GetNativeLeafName(leaf);
+  if (NS_FAILED(rv))
+    return rv;
+  leaf.AppendLiteral(".Trash");
+
+  return (*result)->SetNativeLeafName(leaf);
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsDeleteDir.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* ***** 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 Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Darin Fisher <darin@meer.net>
+ *
+ * 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 ***** */
+
+#ifndef nsDeleteDir_h__
+#define nsDeleteDir_h__
+
+#include "nsCOMPtr.h"
+
+class nsIFile;
+
+/**
+ * This routine attempts to delete a directory that may contain some files that
+ * are still in use.  This later point is only an issue on Windows and a few
+ * other systems.
+ *
+ * If the moveToTrash parameter is true, then the process for deleting the
+ * directory creates a sibling directory of the same name with the ".Trash"
+ * suffix.  It then attempts to move the given directory into the corresponding
+ * trash folder (moving individual files if necessary).  Next, it proceeds to
+ * delete each file in the trash folder on a low-priority background thread.
+ *
+ * If the moveToTrash parameter is false, then the given directory is deleted
+ * directly.
+ *
+ * If the sync flag is true, then the delete operation runs to completion
+ * before this function returns.  Otherwise, deletion occurs asynchronously.
+ */
+NS_HIDDEN_(nsresult) DeleteDir(nsIFile *dir, PRBool moveToTrash, PRBool sync);
+
+/**
+ * This routine returns the trash directory corresponding to the given 
+ * directory.
+ */
+NS_HIDDEN_(nsresult) GetTrashDir(nsIFile *dir, nsCOMPtr<nsIFile> *result);
+
+#endif  // nsDeleteDir_h__
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsDiskCache.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsCacheDevice.h, released
+ * March 9, 2001.
+ *
+ * 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):
+ *   Patrick Beard   <beard@netscape.com>
+ *   Gordon Sheridan <gordon@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 ***** */
+
+
+#ifndef _nsDiskCache_h_
+#define _nsDiskCache_h_
+
+#include "nsCacheEntry.h"
+
+#ifdef XP_WIN
+#include <winsock.h>  // for htonl/ntohl
+#endif
+
+
+class nsDiskCache {
+public:
+    enum {
+            kCurrentVersion = 0x0001000C      // format = 16 bits major version/16 bits minor version
+    };
+
+    enum { kData, kMetaData };
+
+    // Parameter initval initializes internal state of hash function. Hash values are different
+    // for the same text when different initval is used. It can be any random number.
+    // 
+    // It can be used for generating 64-bit hash value:
+    //   (PRUint64(Hash(key, initval1)) << 32) | Hash(key, initval2)
+    //
+    // It can be also used to hash multiple strings:
+    //   h = Hash(string1, 0);
+    //   h = Hash(string2, h);
+    //   ... 
+    static PLDHashNumber    Hash(const char* key, PLDHashNumber initval=0);
+    static nsresult         Truncate(PRFileDesc *  fd, PRUint32  newEOF);
+};
+
+#endif // _nsDiskCache_h_
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsDiskCacheBinding.cpp
@@ -0,0 +1,403 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsDiskCacheBinding.cpp, released
+ * May 10, 2001.
+ *
+ * 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):
+ *   Patrick C. Beard <beard@netscape.com>
+ *   Gordon Sheridan  <gordon@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 <limits.h>
+
+#include "nscore.h"
+#include "nsDiskCacheBinding.h"
+
+
+
+/******************************************************************************
+ *  static hash table callback functions
+ *
+ *****************************************************************************/
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark HASHTABLE CALLBACKS
+#endif
+
+struct HashTableEntry : PLDHashEntryHdr {
+    nsDiskCacheBinding *  mBinding;
+};
+
+
+static PLDHashNumber
+HashKey( PLDHashTable *table, const void *key)
+{
+    return (PLDHashNumber) NS_PTR_TO_INT32(key);
+}
+
+
+static PRBool
+MatchEntry(PLDHashTable *              /* table */,
+            const PLDHashEntryHdr *       header,
+            const void *                  key)
+{
+    HashTableEntry * hashEntry = (HashTableEntry *) header;
+    return (hashEntry->mBinding->mRecord.HashNumber() == (PLDHashNumber) NS_PTR_TO_INT32(key));
+}
+
+static void
+MoveEntry(PLDHashTable *           /* table */,
+          const PLDHashEntryHdr *     src,
+          PLDHashEntryHdr       *     dst)
+{
+    ((HashTableEntry *)dst)->mBinding = ((HashTableEntry *)src)->mBinding;
+}
+
+
+static void
+ClearEntry(PLDHashTable *      /* table */,
+           PLDHashEntryHdr *      header)
+{
+    ((HashTableEntry *)header)->mBinding = nsnull;
+}
+
+
+/******************************************************************************
+ *  Utility Functions
+ *****************************************************************************/
+#ifdef XP_MAC
+#pragma mark -
+#pragma mark DISK CACHE BINDERY
+#endif
+
+nsDiskCacheBinding *
+GetCacheEntryBinding(nsCacheEntry * entry)
+{
+    return (nsDiskCacheBinding *) entry->Data();
+}
+
+
+/******************************************************************************
+ *  nsDiskCacheBinding
+ *****************************************************************************/
+
+NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBinding)
+
+nsDiskCacheBinding::nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record)
+    :   mCacheEntry(entry)
+    ,   mStreamIO(nsnull)
+{
+    NS_ASSERTION(record->ValidRecord(), "bad record");
+    PR_INIT_CLIST(this);
+    mRecord     = *record;
+    mDoomed     = entry->IsDoomed();
+    mGeneration = record->Generation();    // 0 == uninitialized, or data & meta using block files
+}
+
+nsDiskCacheBinding::~nsDiskCacheBinding()
+{
+    NS_ASSERTION(PR_CLIST_IS_EMPTY(this), "binding deleted while still on list");
+    if (!PR_CLIST_IS_EMPTY(this))
+        PR_REMOVE_LINK(this);       // XXX why are we still on a list?
+    
+    // sever streamIO/binding link
+    if (mStreamIO) {
+        mStreamIO->ClearBinding();
+        NS_RELEASE(mStreamIO);
+    }
+}
+
+nsresult
+nsDiskCacheBinding::EnsureStreamIO()
+{
+    if (!mStreamIO) {
+        mStreamIO = new nsDiskCacheStreamIO(this);
+        if (!mStreamIO)  return NS_ERROR_OUT_OF_MEMORY;
+        NS_ADDREF(mStreamIO);
+    }
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  nsDiskCacheBindery
+ *
+ *  Keeps track of bound disk cache entries to detect for collisions.
+ *
+ *****************************************************************************/
+
+PLDHashTableOps nsDiskCacheBindery::ops =
+{
+    PL_DHashAllocTable,
+    PL_DHashFreeTable,
+    HashKey,
+    MatchEntry,
+    MoveEntry,
+    ClearEntry,
+    PL_DHashFinalizeStub
+};
+
+
+nsDiskCacheBindery::nsDiskCacheBindery()
+    : initialized(PR_FALSE)
+{
+}
+
+
+nsDiskCacheBindery::~nsDiskCacheBindery()
+{
+    Reset();
+}
+
+
+nsresult
+nsDiskCacheBindery::Init()
+{
+    nsresult rv = NS_OK;
+    initialized = PL_DHashTableInit(&table, &ops, nsnull, sizeof(HashTableEntry), 0);
+
+    if (!initialized) rv = NS_ERROR_OUT_OF_MEMORY;
+    
+    return rv;
+}
+
+void
+nsDiskCacheBindery::Reset()
+{
+    if (initialized) {
+        PL_DHashTableFinish(&table);
+        initialized = PR_FALSE;
+    }
+}
+
+
+nsDiskCacheBinding *
+nsDiskCacheBindery::CreateBinding(nsCacheEntry *       entry,
+                                  nsDiskCacheRecord *  record)
+{
+    NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
+    nsCOMPtr<nsISupports> data = entry->Data();
+    if (data) {
+        NS_ERROR("cache entry already has bind data");
+        return nsnull;
+    }
+    
+    nsDiskCacheBinding * binding = new nsDiskCacheBinding(entry, record);
+    if (!binding)  return nsnull;
+        
+    // give ownership of the binding to the entry
+    entry->SetData(binding);
+    
+    // add binding to collision detection system
+    nsresult rv = AddBinding(binding);
+    if (NS_FAILED(rv)) {
+        entry->SetData(nsnull);
+        return nsnull;
+    }
+
+    return binding;
+}
+
+
+/**
+ *  FindActiveEntry :  to find active colliding entry so we can doom it
+ */
+nsDiskCacheBinding *
+nsDiskCacheBindery::FindActiveBinding(PRUint32  hashNumber)
+{
+    NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
+    // find hash entry for key
+    HashTableEntry * hashEntry;
+    hashEntry = (HashTableEntry *) PL_DHashTableOperate(&table, (void*) hashNumber, PL_DHASH_LOOKUP);
+    if (PL_DHASH_ENTRY_IS_FREE(hashEntry)) return nsnull;
+    
+    // walk list looking for active entry
+    NS_ASSERTION(hashEntry->mBinding, "hash entry left with no binding");
+    nsDiskCacheBinding * binding = hashEntry->mBinding;    
+    while (binding->mCacheEntry->IsDoomed()) {
+        binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
+        if (binding == hashEntry->mBinding)  return nsnull;
+    }
+    return binding;
+}
+
+
+/**
+ *  AddBinding
+ *
+ *  Called from FindEntry() if we read an entry off of disk
+ *      - it may already have a generation number
+ *      - a generation number conflict is an error
+ *
+ *  Called from BindEntry()
+ *      - a generation number needs to be assigned
+ */
+nsresult
+nsDiskCacheBindery::AddBinding(nsDiskCacheBinding * binding)
+{
+    NS_ENSURE_ARG_POINTER(binding);
+    NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
+
+    // find hash entry for key
+    HashTableEntry * hashEntry;
+    hashEntry = (HashTableEntry *) PL_DHashTableOperate(&table,
+                                                        (void*) binding->mRecord.HashNumber(),
+                                                        PL_DHASH_ADD);
+    if (!hashEntry) return NS_ERROR_OUT_OF_MEMORY;
+    
+    if (hashEntry->mBinding == nsnull) {
+        hashEntry->mBinding = binding;
+        if (binding->mGeneration == 0)
+            binding->mGeneration = 1;   // if generation uninitialized, set it to 1
+            
+        return NS_OK;
+    }
+    
+    
+    // insert binding in generation order
+    nsDiskCacheBinding * p  = hashEntry->mBinding;
+    PRBool   calcGeneration = (binding->mGeneration == 0);  // do we need to calculate generation?
+    if (calcGeneration)  binding->mGeneration = 1;          // initialize to 1 if uninitialized
+    while (1) {
+    
+        if (binding->mGeneration < p->mGeneration) {
+            // here we are
+            PR_INSERT_BEFORE(binding, p);
+            if (hashEntry->mBinding == p)
+                hashEntry->mBinding = binding;
+            break;
+        }
+        
+        if (binding->mGeneration == p->mGeneration) {
+            if (calcGeneration)  ++binding->mGeneration;    // try the next generation
+            else {
+                NS_ERROR("### disk cache: generations collide!");
+                return NS_ERROR_UNEXPECTED;
+            }
+        }
+
+        p = (nsDiskCacheBinding *)PR_NEXT_LINK(p);
+        if (p == hashEntry->mBinding) {
+            // end of line: insert here or die
+            p = (nsDiskCacheBinding *)PR_PREV_LINK(p);  // back up and check generation
+            if (p->mGeneration == 255) {
+                NS_WARNING("### disk cache: generation capacity at full");
+                return NS_ERROR_UNEXPECTED;
+            }
+            PR_INSERT_BEFORE(binding, hashEntry->mBinding);
+            break;
+        }
+    }
+    return NS_OK;
+}
+
+
+/**
+ *  RemoveBinding :  remove binding from collision detection on deactivation
+ */
+void
+nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding)
+{
+    NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
+    if (!initialized)   return;
+    
+    HashTableEntry * hashEntry;
+    void *           key = (void *)binding->mRecord.HashNumber();
+
+    hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table,
+                                                       (void*) key,
+                                                       PL_DHASH_LOOKUP);
+    if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry)) {
+        NS_WARNING("### disk cache: binding not in hashtable!");
+        return;
+    }
+    
+    if (binding == hashEntry->mBinding) {
+        if (PR_CLIST_IS_EMPTY(binding)) {
+            // remove this hash entry
+            (void) PL_DHashTableOperate(&table,
+                                        (void*) binding->mRecord.HashNumber(),
+                                        PL_DHASH_REMOVE);
+            return;
+            
+        } else {
+            // promote next binding to head, and unlink this binding
+            hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
+        }
+    }
+    PR_REMOVE_AND_INIT_LINK(binding);
+}
+
+
+/**
+ *  ActiveBinding : PLDHashTable enumerate function to verify active bindings
+ */
+
+PLDHashOperator
+ActiveBinding(PLDHashTable *    table,
+              PLDHashEntryHdr * hdr,
+              PRUint32          number,
+              void *            arg)
+{
+    nsDiskCacheBinding * binding = ((HashTableEntry *)hdr)->mBinding;
+    NS_ASSERTION(binding, "### disk cache binding = nsnull!");
+    
+    nsDiskCacheBinding * head = binding;
+    do {   
+        if (binding->IsActive()) {
+           *((PRBool *)arg) = PR_TRUE;
+            return PL_DHASH_STOP;
+        }
+
+        binding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding);
+    } while (binding != head);
+
+    return PL_DHASH_NEXT;
+}
+
+
+/**
+ *  ActiveBindings : return PR_TRUE if any bindings have open descriptors
+ */
+PRBool
+nsDiskCacheBindery::ActiveBindings()
+{
+    NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized");
+    if (!initialized) return PR_FALSE;
+
+    PRBool  activeBinding = PR_FALSE;
+    PL_DHashTableEnumerate(&table, ActiveBinding, &activeBinding);
+
+    return activeBinding;
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsDiskCacheBinding.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsDiskCacheBinding.h, released
+ * May 10, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan  <gordon@netscape.com>
+ *   Patrick C. Beard <beard@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 ***** */
+
+
+#ifndef _nsDiskCacheBinding_h_
+#define _nsDiskCacheBinding_h_
+
+#include "nspr.h"
+#include "pldhash.h"
+
+#include "nsISupports.h"
+#include "nsCacheEntry.h"
+
+#include "nsDiskCacheMap.h"
+#include "nsDiskCacheStreams.h"
+
+
+/******************************************************************************
+ *  nsDiskCacheBinding
+ *
+ *  Created for disk cache specific data and stored in nsCacheEntry.mData as
+ *  an nsISupports.  Also stored in nsDiskCacheHashTable, with collisions
+ *  linked by the PRCList.
+ *
+ *****************************************************************************/
+
+class nsDiskCacheBinding : public nsISupports, public PRCList {
+public:
+    NS_DECL_ISUPPORTS
+
+    nsDiskCacheBinding(nsCacheEntry* entry, nsDiskCacheRecord * record);
+    virtual ~nsDiskCacheBinding();
+
+    nsresult EnsureStreamIO();
+    PRBool   IsActive() { return mCacheEntry != nsnull;}
+
+// XXX make friends
+public:
+    nsCacheEntry*           mCacheEntry;    // back pointer to parent nsCacheEntry
+    nsDiskCacheRecord       mRecord;
+    nsDiskCacheStreamIO*    mStreamIO;      // strong reference
+    PRBool                  mDoomed;        // record is not stored in cache map
+    PRUint8                 mGeneration;    // possibly just reservation
+};
+
+
+/******************************************************************************
+ *  Utility Functions
+ *****************************************************************************/
+
+nsDiskCacheBinding *   GetCacheEntryBinding(nsCacheEntry * entry);
+
+
+
+/******************************************************************************
+ *  nsDiskCacheBindery
+ *
+ *  Used to keep track of nsDiskCacheBinding associated with active/bound (and
+ *  possibly doomed) entries.  Lookups on 4 byte disk hash to find collisions
+ *  (which need to be doomed, instead of just evicted.  Collisions are linked
+ *  using a PRCList to keep track of current generation number.
+ *
+ *  Used to detect hash number collisions, and find available generation numbers.
+ *
+ *  Not all nsDiskCacheBinding have a generation number.
+ *
+ *  Generation numbers may be aquired late, or lost (when data fits in block file)
+ *
+ *  Collisions can occur:
+ *      BindEntry()       - hashnumbers collide (possibly different keys)
+ *
+ *  Generation number required:
+ *      DeactivateEntry() - metadata written to disk, may require file
+ *      GetFileForEntry() - force data to require file
+ *      writing to stream - data size may require file
+ *
+ *  Binding can be kept in PRCList in order of generation numbers.
+ *  Binding with no generation number can be Appended to PRCList (last).
+ *
+ *****************************************************************************/
+
+class nsDiskCacheBindery {
+public:
+    nsDiskCacheBindery();
+    ~nsDiskCacheBindery();
+
+    nsresult                Init();
+    void                    Reset();
+
+    nsDiskCacheBinding *    CreateBinding(nsCacheEntry *       entry,
+                                          nsDiskCacheRecord *  record);
+
+    nsDiskCacheBinding *    FindActiveBinding(PRUint32  hashNumber);
+    void                    RemoveBinding(nsDiskCacheBinding * binding);
+    PRBool                  ActiveBindings();
+    
+private:
+    nsresult                AddBinding(nsDiskCacheBinding * binding);
+
+    // member variables
+    static PLDHashTableOps ops;
+    PLDHashTable           table;
+    PRBool                 initialized;
+};
+
+#endif /* _nsDiskCacheBinding_h_ */
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsDiskCacheBlockFile.cpp
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsDiskCacheBlockFile.cpp, released
+ * April 12, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan  <gordon@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 "nsDiskCache.h"
+#include "nsDiskCacheBlockFile.h"
+
+/******************************************************************************
+ * nsDiskCacheBlockFile - 
+ *****************************************************************************/
+
+const unsigned short kBitMapBytes = 4096;
+const unsigned short kBitMapWords = (kBitMapBytes/4);
+
+/******************************************************************************
+ *  Open
+ *****************************************************************************/
+nsresult
+nsDiskCacheBlockFile::Open( nsILocalFile *  blockFile, PRUint32  blockSize)
+{
+    PRInt32   fileSize;
+
+    mBlockSize = blockSize;
+    
+    // open the file - restricted to user, the data could be confidential
+    nsresult rv = blockFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE, 00600, &mFD);
+    if (NS_FAILED(rv))  return rv;  // unable to open or create file
+    
+    // allocate bit map buffer
+    mBitMap = new PRUint32[kBitMapWords];
+    if (!mBitMap) {
+        rv = NS_ERROR_OUT_OF_MEMORY;
+        goto error_exit;
+    }
+    
+    // check if we just creating the file
+    fileSize = PR_Available(mFD);
+    if (fileSize < 0) {
+        // XXX an error occurred. We could call PR_GetError(), but how would that help?
+        rv = NS_ERROR_UNEXPECTED;
+        goto error_exit;
+    }
+    if (fileSize == 0) {
+        // initialize bit map and write it
+        memset(mBitMap, 0, kBitMapBytes);
+        PRInt32 bytesWritten = PR_Write(mFD, mBitMap, kBitMapBytes);
+        if (bytesWritten < kBitMapBytes) 
+            goto error_exit;
+        
+    } else if (fileSize < kBitMapBytes) {
+        rv = NS_ERROR_UNEXPECTED;  // XXX NS_ERROR_CACHE_INVALID;
+        goto error_exit;
+        
+    } else {
+        // read the bit map
+        const PRInt32 bytesRead = PR_Read(mFD, mBitMap, kBitMapBytes);
+        if (bytesRead < kBitMapBytes) {
+            rv = NS_ERROR_UNEXPECTED;
+            goto error_exit;
+        }
+#if defined(IS_LITTLE_ENDIAN)
+        // Swap from network format
+        for (int i = 0; i < kBitMapWords; ++i)
+            mBitMap[i] = ntohl(mBitMap[i]);
+#endif
+        // validate block file size
+        // Because not whole blocks are written, the size may be a 
+        // little bit smaller than used blocks times blocksize,
+        // because the last block will generally not be 'whole'.
+        const PRUint32  estimatedSize = CalcBlockFileSize();
+        if ((PRUint32)fileSize + blockSize < estimatedSize) {
+            rv = NS_ERROR_UNEXPECTED;
+            goto error_exit;
+        }
+    }
+    return NS_OK;
+
+error_exit:
+    Close(PR_FALSE);
+    return rv;
+}
+
+
+/******************************************************************************
+ *  Close
+ *****************************************************************************/
+nsresult
+nsDiskCacheBlockFile::Close(PRBool flush)
+{
+    nsresult rv = NS_OK;
+
+    if (mFD) {
+        if (flush)
+            rv  = FlushBitMap();
+        PRStatus err = PR_Close(mFD);
+        if (NS_SUCCEEDED(rv) && (err != PR_SUCCESS))
+            rv = NS_ERROR_UNEXPECTED;
+        mFD = nsnull;
+    }
+
+     if (mBitMap) {
+         delete [] mBitMap;
+         mBitMap = nsnull;
+     }
+        
+    return rv;
+}
+
+
+/******************************************************************************
+ *  AllocateBlocks
+ *
+ *  Allocates 1-4 blocks, using a first fit strategy,
+ *  so that no group of blocks spans a quad block boundary.
+ *
+ *  Returns block number of first block allocated or -1 on failure.
+ *
+ *****************************************************************************/
+PRInt32
+nsDiskCacheBlockFile::AllocateBlocks(PRInt32 numBlocks)
+{
+    const int maxPos = 32 - numBlocks;
+    const PRUint32 mask = (0x01 << numBlocks) - 1;
+    for (int i = 0; i < kBitMapWords; ++i) {
+        PRUint32 mapWord = ~mBitMap[i]; // flip bits so free bits are 1
+        if (mapWord) {                  // At least one free bit
+            // Binary search for first free bit in word
+            int bit = 0;
+            if ((mapWord & 0x0FFFF) == 0) { bit |= 16; mapWord >>= 16; }
+            if ((mapWord & 0x000FF) == 0) { bit |= 8;  mapWord >>= 8;  }
+            if ((mapWord & 0x0000F) == 0) { bit |= 4;  mapWord >>= 4;  }
+            if ((mapWord & 0x00003) == 0) { bit |= 2;  mapWord >>= 2;  }
+            if ((mapWord & 0x00001) == 0) { bit |= 1;  mapWord >>= 1;  }
+            // Find first fit for mask
+            for (; bit <= maxPos; ++bit) {
+                // all bits selected by mask are 1, so free
+                if ((mask & mapWord) == mask) {
+                    mBitMap[i] |= mask << bit; 
+                    mBitMapDirty = PR_TRUE;
+                    return i * 32 + bit;
+                }
+            }
+        }
+    }
+    
+    return -1;
+}
+
+
+/******************************************************************************
+ *  DeallocateBlocks
+ *****************************************************************************/
+nsresult
+nsDiskCacheBlockFile::DeallocateBlocks( PRInt32  startBlock, PRInt32  numBlocks)
+{
+    if (!mFD)  return NS_ERROR_NOT_AVAILABLE;
+
+    if ((startBlock < 0) || (startBlock > kBitMapBytes * 8 - 1) ||
+        (numBlocks < 1)  || (numBlocks > 4))
+       return NS_ERROR_ILLEGAL_VALUE;
+           
+    const PRInt32 startWord = startBlock >> 5;      // Divide by 32
+    const PRUint32 startBit = startBlock & 31;      // Modulo by 32 
+      
+    // make sure requested deallocation doesn't span a word boundary
+    if (startBit + numBlocks > 32)  return NS_ERROR_UNEXPECTED;
+    PRUint32 mask = ((0x01 << numBlocks) - 1) << startBit;
+    
+    // make sure requested deallocation is currently allocated
+    if ((mBitMap[startWord] & mask) != mask)    return NS_ERROR_ABORT;
+
+    mBitMap[startWord] ^= mask;    // flips the bits off;
+    mBitMapDirty = PR_TRUE;
+    // XXX rv = FlushBitMap();  // coherency vs. performance
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  WriteBlocks
+ *****************************************************************************/
+nsresult
+nsDiskCacheBlockFile::WriteBlocks( void *   buffer,
+                                   PRUint32 size,
+                                   PRInt32  numBlocks,
+                                   PRInt32 * startBlock)
+{
+    // presume buffer != nsnull and startBlock != nsnull
+    NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_AVAILABLE);
+    
+    // allocate some blocks in the cache block file
+    *startBlock = AllocateBlocks(numBlocks);
+    NS_ENSURE_STATE(*startBlock >= 0);
+    
+    // seek to block position
+    PRInt32 blockPos = kBitMapBytes + *startBlock * mBlockSize;
+    PRInt32 filePos = PR_Seek(mFD, blockPos, PR_SEEK_SET);
+    NS_ENSURE_STATE(filePos == blockPos);
+    
+    // write the blocks
+    PRInt32 bytesWritten = PR_Write(mFD, buffer, size);
+    NS_ENSURE_STATE(bytesWritten >= 0 && PRUint32(bytesWritten) == size);
+    
+    // write the bit map and flush the file
+    // XXX except we would take a severe performance hit
+    // XXX rv = FlushBitMap();
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  ReadBlocks
+ *****************************************************************************/
+nsresult
+nsDiskCacheBlockFile::ReadBlocks( void *    buffer,
+                                  PRInt32   startBlock,
+                                  PRInt32   numBlocks,
+                                  PRInt32 * bytesRead)
+{
+    // presume buffer != nsnull and bytesRead != bytesRead
+
+    if (!mFD)  return NS_ERROR_NOT_AVAILABLE;
+    nsresult rv = VerifyAllocation(startBlock, numBlocks);
+    if (NS_FAILED(rv))  return rv;
+    
+    // seek to block position
+    PRInt32 blockPos = kBitMapBytes + startBlock * mBlockSize;
+    PRInt32 filePos = PR_Seek(mFD, blockPos, PR_SEEK_SET);
+    if (filePos != blockPos)  return NS_ERROR_UNEXPECTED;
+
+    // read the blocks
+    PRInt32 bytesToRead = *bytesRead;
+    if ((bytesToRead <= 0) || ((PRUint32)bytesToRead > mBlockSize * numBlocks)) {
+        bytesToRead = mBlockSize * numBlocks;
+    }
+    *bytesRead = PR_Read(mFD, buffer, bytesToRead);
+    
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  FlushBitMap
+ *****************************************************************************/
+nsresult
+nsDiskCacheBlockFile::FlushBitMap()
+{
+    if (!mBitMapDirty)  return NS_OK;
+
+    // seek to bitmap
+    PRInt32 filePos = PR_Seek(mFD, 0, PR_SEEK_SET);
+    if (filePos != 0)  return NS_ERROR_UNEXPECTED;
+    
+#if defined(IS_LITTLE_ENDIAN)
+    PRUint32 bitmap[kBitMapWords];
+    // Copy and swap to network format
+    PRUint32 *p = bitmap;
+    for (int i = 0; i < kBitMapWords; ++i, ++p)
+      *p = htonl(mBitMap[i]);
+#else
+    PRUint32 *bitmap = mBitMap;
+#endif
+
+    // write bitmap
+    PRInt32 bytesWritten = PR_Write(mFD, bitmap, kBitMapBytes);
+    if (bytesWritten < kBitMapBytes)  return NS_ERROR_UNEXPECTED;
+
+    PRStatus err = PR_Sync(mFD);
+    if (err != PR_SUCCESS)  return NS_ERROR_UNEXPECTED;
+
+    mBitMapDirty = PR_FALSE;
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  VerifyAllocation
+ *
+ *  Return values:
+ *      NS_OK if all bits are marked allocated
+ *      NS_ERROR_ILLEGAL_VALUE if parameters don't obey constraints
+ *      NS_ERROR_FAILURE if some or all the bits are marked unallocated
+ *
+ *****************************************************************************/
+nsresult
+nsDiskCacheBlockFile::VerifyAllocation( PRInt32  startBlock, PRInt32  numBlocks)
+{
+    if ((startBlock < 0) || (startBlock > kBitMapBytes * 8 - 1) ||
+        (numBlocks < 1)  || (numBlocks > 4))
+       return NS_ERROR_ILLEGAL_VALUE;
+    
+    const PRInt32 startWord = startBlock >> 5;      // Divide by 32
+    const PRUint32 startBit = startBlock & 31;      // Modulo by 32 
+      
+    // make sure requested deallocation doesn't span a word boundary
+    if (startBit + numBlocks > 32)  return NS_ERROR_ILLEGAL_VALUE;
+    PRUint32 mask = ((0x01 << numBlocks) - 1) << startBit;
+    
+    // check if all specified blocks are currently allocated
+    if ((mBitMap[startWord] & mask) != mask)    return NS_ERROR_FAILURE;
+    
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  CalcBlockFileSize
+ *
+ *  Return size of the block file according to the bits set in mBitmap
+ *
+ *****************************************************************************/
+PRUint32
+nsDiskCacheBlockFile::CalcBlockFileSize()
+{
+    // search for last byte in mBitMap with allocated bits
+    PRUint32  estimatedSize = kBitMapBytes;      
+    PRInt32   i = kBitMapWords;
+    while (--i >= 0) {
+        if (mBitMap[i]) break;
+    }
+
+    if (i >= 0) {
+        // binary search to find last allocated bit in byte
+        PRUint32 mapWord = mBitMap[i];
+        PRUint32 lastBit = 31;
+        if ((mapWord & 0xFFFF0000) == 0) { lastBit ^= 16; mapWord <<= 16; }
+        if ((mapWord & 0xFF000000) == 0) { lastBit ^= 8; mapWord <<= 8; }
+        if ((mapWord & 0xF0000000) == 0) { lastBit ^= 4; mapWord <<= 4; }
+        if ((mapWord & 0xC0000000) == 0) { lastBit ^= 2; mapWord <<= 2; }
+        if ((mapWord & 0x80000000) == 0) { lastBit ^= 1; mapWord <<= 1; }
+        estimatedSize +=  (i * 32 + lastBit + 1) * mBlockSize;
+    }
+
+    return estimatedSize;
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsDiskCacheBlockFile.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsDiskCacheBlockFile.h, released
+ * April 12, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan  <gordon@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 ***** */
+
+#ifndef _nsDiskCacheBlockFile_h_
+#define _nsDiskCacheBlockFile_h_
+
+#include "nsILocalFile.h"
+
+/******************************************************************************
+ *  nsDiskCacheBlockFile
+ *
+ *  The structure of a cache block file is a 4096 bytes bit map, followed by
+ *  some number of blocks of mBlockSize.  The creator of a
+ *  nsDiskCacheBlockFile object must provide the block size for a given file.
+ *
+ *****************************************************************************/
+class nsDiskCacheBlockFile {
+public:
+    nsDiskCacheBlockFile()
+           : mFD(nsnull)
+           , mBlockSize(0)
+           , mBitMap(nsnull)
+           , mBitMapDirty(PR_FALSE)
+            {}
+    ~nsDiskCacheBlockFile() { (void) Close(PR_TRUE); }
+    
+    nsresult  Open( nsILocalFile *  blockFile, PRUint32  blockSize);
+    nsresult  Close(PRBool flush);
+    
+    /*
+     * Trim
+     * Truncates the block file to the end of the last allocated block.
+     */
+    nsresult  Trim() { return nsDiskCache::Truncate(mFD, CalcBlockFileSize()); }
+    nsresult  DeallocateBlocks( PRInt32  startBlock, PRInt32  numBlocks);
+    nsresult  WriteBlocks( void * buffer, PRUint32 size, PRInt32  numBlocks, 
+                           PRInt32 * startBlock);
+    nsresult  ReadBlocks( void * buffer, PRInt32  startBlock, PRInt32  numBlocks, 
+                          PRInt32 * bytesRead);
+    
+private:
+    nsresult  FlushBitMap();
+    PRInt32   AllocateBlocks( PRInt32  numBlocks);
+    nsresult  VerifyAllocation( PRInt32 startBlock, PRInt32 numBLocks);
+    PRUint32  CalcBlockFileSize();
+
+/**
+ *  Data members
+ */
+    PRFileDesc *                mFD;
+    PRUint32                    mBlockSize;
+    PRUint32 *                  mBitMap;      // XXX future: array of bit map blocks
+    PRBool                      mBitMapDirty;
+};
+
+#endif // _nsDiskCacheBlockFile_h_
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/nsDiskCacheDevice.cpp
@@ -0,0 +1,1055 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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 nsDiskCacheDevice.cpp, released
+ * February 22, 2001.
+ *
+ * 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):
+ *   Gordon Sheridan <gordon@netscape.com>
+ *   Patrick C. Beard <beard@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 <limits.h>
+
+// include files for ftruncate (or equivalent)
+#if defined(XP_UNIX) || defined(XP_BEOS)
+#include <unistd.h>
+#elif defined(XP_WIN)
+#include <windows.h>
+#elif defined(XP_OS2)
+#define INCL_DOSERRORS
+#include <os2.h>
+#else
+// XXX add necessary include file for ftruncate (or equivalent)
+#endif
+
+#include "prtypes.h"
+#include "prthread.h"
+#include "prbit.h"
+
+#include "private/pprio.h"
+
+#include "nsDiskCacheDevice.h"
+#include "nsDiskCacheEntry.h"
+#include "nsDiskCacheMap.h"
+#include "nsDiskCacheStreams.h"
+
+#include "nsDiskCache.h"
+
+#include "nsCacheService.h"
+#include "nsCache.h"
+
+#include "nsDeleteDir.h"
+
+#include "nsICacheVisitor.h"
+#include "nsReadableUtils.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsAutoLock.h"
+#include "nsCRT.h"
+#include "nsCOMArray.h"
+#include "nsISimpleEnumerator.h"
+
+#include "mozilla/FunctionTimer.h"
+
+static const char DISK_CACHE_DEVICE_ID[] = { "disk" };
+
+
+/******************************************************************************
+ *  nsDiskCacheEvictor
+ *
+ *  Helper class for nsDiskCacheDevice.
+ *
+ *****************************************************************************/
+
+class nsDiskCacheEvictor : public nsDiskCacheRecordVisitor
+{
+public:
+    nsDiskCacheEvictor( nsDiskCacheMap *      cacheMap,
+                        nsDiskCacheBindery *  cacheBindery,
+                        PRUint32              targetSize,
+                        const char *          clientID)
+        : mCacheMap(cacheMap)
+        , mBindery(cacheBindery)
+        , mTargetSize(targetSize)
+        , mClientID(clientID)
+    { 
+        mClientIDSize = clientID ? strlen(clientID) : 0;
+    }
+    
+    virtual PRInt32  VisitRecord(nsDiskCacheRecord *  mapRecord);
+ 
+private:
+        nsDiskCacheMap *     mCacheMap;
+        nsDiskCacheBindery * mBindery;
+        PRUint32             mTargetSize;
+        const char *         mClientID;
+        PRUint32             mClientIDSize;
+};
+
+
+PRInt32
+nsDiskCacheEvictor::VisitRecord(nsDiskCacheRecord *  mapRecord)
+{
+    if (mCacheMap->TotalSize() < mTargetSize)
+        return kStopVisitingRecords;
+    
+    if (mClientID) {
+        // we're just evicting records for a specific client
+        nsDiskCacheEntry * diskEntry = mCacheMap->ReadDiskCacheEntry(mapRecord);
+        if (!diskEntry)
+            return kVisitNextRecord;  // XXX or delete record?
+    
+        // Compare clientID's without malloc
+        if ((diskEntry->mKeySize <= mClientIDSize) ||
+            (diskEntry->Key()[mClientIDSize] != ':') ||
+            (memcmp(diskEntry->Key(), mClientID, mClientIDSize) != 0)) {
+            return kVisitNextRecord;  // clientID doesn't match, skip it
+        }
+    }
+    
+    nsDiskCacheBinding * binding = mBindery->FindActiveBinding(mapRecord->HashNumber());
+    if (binding) {
+        // We are currently using this entry, so all we can do is doom it.
+        // Since we're enumerating the records, we don't want to call
+        // DeleteRecord when nsCacheService::DoomEntry() calls us back.
+        binding->mDoomed = PR_TRUE;         // mark binding record as 'deleted'
+        nsCacheService::DoomEntry(binding->mCacheEntry);
+    } else {
+        // entry not in use, just delete storage because we're enumerating the records
+        (void) mCacheMap->DeleteStorage(mapRecord);
+    }
+
+    return kDeleteRecordAndContinue;  // this will REALLY delete the record
+}
+
+
+/******************************************************************************
+ *  nsDiskCacheDeviceInfo
+ *****************************************************************************/
+
+class nsDiskCacheDeviceInfo : public nsICacheDeviceInfo {
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICACHEDEVICEINFO
+
+    nsDiskCacheDeviceInfo(nsDiskCacheDevice* device)
+        :   mDevice(device)
+    {
+    }
+
+    virtual ~nsDiskCacheDeviceInfo() {}
+    
+private:
+    nsDiskCacheDevice* mDevice;
+};
+
+NS_IMPL_ISUPPORTS1(nsDiskCacheDeviceInfo, nsICacheDeviceInfo)
+
+/* readonly attribute string description; */
+NS_IMETHODIMP nsDiskCacheDeviceInfo::GetDescription(char ** aDescription)
+{
+    NS_ENSURE_ARG_POINTER(aDescription);
+    *aDescription = NS_strdup("Disk cache device");
+    return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+/* readonly attribute string usageReport; */
+NS_IMETHODIMP nsDiskCacheDeviceInfo::GetUsageReport(char ** usageReport)
+{
+    NS_ENSURE_ARG_POINTER(usageReport);
+    nsCString buffer;
+    
+    buffer.AssignLiteral("\n<tr>\n<td><b>Cache Directory:</b></td>\n<td><tt> ");
+    nsCOMPtr<nsILocalFile> cacheDir;
+    nsAutoString           path;
+    mDevice->getCacheDirectory(getter_AddRefs(cacheDir)); 
+    nsresult rv = cacheDir->GetPath(path);
+    if (NS_SUCCEEDED(rv)) {
+        AppendUTF16toUTF8(path, buffer);
+    } else {
+        buffer.AppendLiteral("directory unavailable");
+    }
+    buffer.AppendLiteral("</tt></td>\n</tr>\n");
+    // buffer.Append("<tr><td><b>Files:</b></td><td><tt> XXX</tt></td></tr>");
+    *usageReport = ToNewCString(buffer);
+    if (!*usageReport) return NS_ERROR_OUT_OF_MEMORY;
+
+    return NS_OK;
+}
+
+/* readonly attribute unsigned long entryCount; */
+NS_IMETHODIMP nsDiskCacheDeviceInfo::GetEntryCount(PRUint32 *aEntryCount)
+{
+    NS_ENSURE_ARG_POINTER(aEntryCount);
+    *aEntryCount = mDevice->getEntryCount();
+    return NS_OK;
+}
+
+/* readonly attribute unsigned long totalSize; */
+NS_IMETHODIMP nsDiskCacheDeviceInfo::GetTotalSize(PRUint32 *aTotalSize)
+{
+    NS_ENSURE_ARG_POINTER(aTotalSize);
+    // Returned unit's are in bytes
+    *aTotalSize = mDevice->getCacheSize() * 1024;
+    return NS_OK;
+}
+
+/* readonly attribute unsigned long maximumSize; */
+NS_IMETHODIMP nsDiskCacheDeviceInfo::GetMaximumSize(PRUint32 *aMaximumSize)
+{
+    NS_ENSURE_ARG_POINTER(aMaximumSize);
+    // Returned unit's are in bytes
+    *aMaximumSize = mDevice->getCacheCapacity() * 1024;
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  nsDiskCache
+ *****************************************************************************/
+
+/**
+ *  nsDiskCache::Hash(const char * key, PLDHashNumber initval)
+ *
+ *  See http://burtleburtle.net/bob/hash/evahash.html for more information
+ *  about this hash function.
+ *
+ *  This algorithm of this method implies nsDiskCacheRecords will be stored
+ *  in a certain order on disk.  If the algorithm changes, existing cache
+ *  map files may become invalid, and therefore the kCurrentVersion needs
+ *  to be revised.
+ */
+
+static inline void hashmix(PRUint32& a, PRUint32& b, PRUint32& c)
+{
+  a -= b; a -= c; a ^= (c>>13);
+  b -= c; b -= a; b ^= (a<<8);
+  c -= a; c -= b; c ^= (b>>13);
+  a -= b; a -= c; a ^= (c>>12); 
+  b -= c; b -= a; b ^= (a<<16);
+  c -= a; c -= b; c ^= (b>>5);
+  a -= b; a -= c; a ^= (c>>3);
+  b -= c; b -= a; b ^= (a<<10);
+  c -= a; c -= b; c ^= (b>>15);
+}
+
+PLDHashNumber
+nsDiskCache::Hash(const char * key, PLDHashNumber initval)
+{
+  const PRUint8 *k = reinterpret_cast<const PRUint8*>(key);
+  PRUint32 a, b, c, len, length;
+
+  length = PL_strlen(key);
+  /* Set up the internal state */
+  len = length;
+  a = b = 0x9e3779b9;  /* the golden ratio; an arbitrary value */
+  c = initval;         /* variable initialization of internal state */
+
+  /*---------------------------------------- handle most of the key */
+  while (len >= 12)
+  {
+    a += k[0] + (PRUint32(k[1])<<8) + (PRUint32(k[2])<<16) + (PRUint32(k[3])<<24);
+    b += k[4] + (PRUint32(k[5])<<8) + (PRUint32(k[6])<<16) + (PRUint32(k[7])<<24);
+    c += k[8] + (PRUint32(k[9])<<8) + (PRUint32(k[10])<<16) + (PRUint32(k[11])<<24);
+    hashmix(a, b, c);
+    k += 12; len -= 12;
+  }
+
+  /*------------------------------------- handle the last 11 bytes */
+  c += length;
+  switch(len) {              /* all the case statements fall through */
+    case 11: c += (PRUint32(k[10])<<24);
+    case 10: c += (PRUint32(k[9])<<16);
+    case 9 : c += (PRUint32(k[8])<<8);
+    /* the low-order byte of c is reserved for the length */
+    case 8 : b += (PRUint32(k[7])<<24);
+    case 7 : b += (PRUint32(k[6])<<16);
+    case 6 : b += (PRUint32(k[5])<<8);
+    case 5 : b += k[4];
+    case 4 : a += (PRUint32(k[3])<<24);
+    case 3 : a += (PRUint32(k[2])<<16);
+    case 2 : a += (PRUint32(k[1])<<8);
+    case 1 : a += k[0];
+    /* case 0: nothing left to add */
+  }
+  hashmix(a, b, c);
+
+  return c;
+}
+
+nsresult
+nsDiskCache::Truncate(PRFileDesc *  fd, PRUint32  newEOF)
+{
+    // use modified SetEOF from nsFileStreams::SetEOF()
+
+#if defined(XP_UNIX) || defined(XP_BEOS)
+    if (ftruncate(PR_FileDesc2NativeHandle(fd), newEOF) != 0) {
+        NS_ERROR("ftruncate failed");
+        return NS_ERROR_FAILURE;
+    }
+
+#elif defined(XP_WIN)
+    PRInt32 cnt = PR_Seek(fd, newEOF, PR_SEEK_SET);
+    if (cnt == -1)  return NS_ERROR_FAILURE;
+    if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(fd))) {
+        NS_ERROR("SetEndOfFile failed");
+        return NS_ERROR_FAILURE;
+    }
+
+#elif defined(XP_OS2)
+    if (DosSetFileSize((HFILE) PR_FileDesc2NativeHandle(fd), newEOF) != NO_ERROR) {
+        NS_ERROR("DosSetFileSize failed");
+        return NS_ERROR_FAILURE;
+    }
+#else
+    // add implementations for other platforms here
+#endif
+    return NS_OK;
+}
+
+
+/******************************************************************************
+ *  nsDiskCacheDevice
+ *****************************************************************************/
+
+nsDiskCacheDevice::nsDiskCacheDevice()
+    : mCacheCapacity(0)
+    , mInitialized(PR_FALSE)
+{
+}
+
+nsDiskCacheDevice::~nsDiskCacheDevice()
+{
+    Shutdown();
+}
+
+
+/**
+ *  methods of nsCacheDevice
+ */
+nsresult
+nsDiskCacheDevice::Init()
+{
+    NS_TIME_FUNCTION;
+
+    nsresult rv;
+
+    NS_ENSURE_TRUE(!Initialized(), NS_ERROR_FAILURE);
+       
+    if (!mCacheDirectory)
+        return NS_ERROR_FAILURE;
+
+    rv = mBindery.Init();
+    if (NS_FAILED(rv))
+        return rv;
+    
+    // Open Disk Cache
+    rv = OpenDiskCache();
+    if (NS_FAILED(rv)) {
+        (void) mCacheMap.Close(PR_FALSE);
+        return rv;
+    }
+
+    mInitialized = PR_TRUE;
+    return NS_OK;
+}
+
+
+/**
+ *  NOTE: called while holding the cache service lock
+ */
+nsresult
+nsDiskCacheDevice::Shutdown()
+{
+    nsresult rv = Shutdown_Private(PR_TRUE);
+    if (NS_FAILED(rv))
+        return rv;
+
+    if (mCacheDirectory) {
+        // delete any trash files left-over before shutting down.
+        nsCOMPtr<nsIFile> trashDir;
+        GetTrashDir(mCacheDirectory, &trashDir);
+        if (trashDir) {
+            PRBool exists;
+            if (NS_SUCCEEDED(trashDir->Exists(&exists)) && exists)
+                DeleteDir(trashDir, PR_FALSE, PR_TRUE);
+        }
+    }
+
+    return NS_OK;
+}
+
+
+nsresult
+nsDiskCacheDevice::Shutdown_Private(PRBool  flush)
+{
+    CACHE_LOG_DEBUG(("CACHE: disk Shutdown_Private [%u]\n", flush));
+
+    if (Initialized()) {
+        // check cache limits in case we need to evict.
+        EvictDiskCacheEntries(mCacheCapacity);
+
+        // write out persistent information about the cache.
+        (void) mCacheMap.Close(flush);
+
+        mBindery.Reset();
+
+        mInitialized = PR_FALSE;
+    }
+
+    return NS_OK;
+}
+
+
+const char *
+nsDiskCacheDevice::GetDeviceID()
+{
+    return DISK_CACHE_DEVICE_ID;
+}
+
+
+/**
+ *  FindEntry -
+ *
+ *      cases:  key not in disk cache, hash number free
+ *              key not in disk cache, hash number used
+ *              key in disk cache
+ *
+ *  NOTE: called while holding the cache service lock
+ */
+nsCacheEntry *
+nsDiskCacheDevice::FindEntry(nsCString * key, PRBool *collision)
+{
+    if (!Initialized())  return nsnull;  // NS_ERROR_NOT_INITIALIZED
+    nsDiskCacheRecord       record;
+    nsDiskCacheBinding *    binding = nsnull;
+    PLDHashNumber           hashNumber = nsDiskCache::Hash(key->get());
+
+    *collision = PR_FALSE;
+
+    binding = mBindery.FindActiveBinding(hashNumber);
+    if (binding && !binding->mCacheEntry->Key()->Equals(*key)) {
+        *collision = PR_TRUE;
+        return nsnull;
+    }
+    binding = nsnull;
+
+    // lookup hash number in cache map
+    nsresult rv = mCacheMap.FindRecord(hashNumber, &record);
+    if (NS_FAILED(rv))  return nsnull;  // XXX log error?
+    
+    nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record);
+    if (!diskEntry) return nsnull;
+    
+    // compare key to be sure
+    if (!key->Equals(diskEntry->Key())) {
+        *collision = PR_TRUE;
+        return nsnull;
+    }
+    
+    nsCacheEntry * entry = diskEntry->CreateCacheEntry(this);
+    if (!entry)  return nsnull;
+    
+    binding = mBindery.CreateBinding(entry, &record);
+    if (!binding) {
+        delete entry;
+        return nsnull;
+    }
+    
+    return entry;
+}
+
+
+/**
+ *  NOTE: called while holding the cache service lock
+ */
+nsresult
+nsDiskCacheDevice::DeactivateEntry(nsCacheEntry * entry)
+{
+    nsresult              rv = NS_OK;
+    nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
+    NS_ASSERTION(binding, "DeactivateEntry: binding == nsnull");
+    if (!binding)  return NS_ERROR_UNEXPECTED;
+
+    CACHE_LOG_DEBUG(("CACHE: disk DeactivateEntry [%p %x]\n",
+        entry, binding->mRecord.HashNumber()));
+
+    if (entry->IsDoomed()) {
+        // delete data, entry, record from disk for entry
+        rv = mCacheMap.DeleteStorage(&binding->mRecord);
+
+    } else {
+        // save stuff to disk for entry
+        rv = mCacheMap.WriteDiskCacheEntry(binding);
+        if (NS_FAILED(rv)) {
+            // clean up as best we can
+            (void) mCacheMap.DeleteStorage(&binding->mRecord);
+            (void) mCacheMap.DeleteRecord(&binding->mRecord);
+            binding->mDoomed = PR_TRUE; // record is no longer in cache map
+        }
+    }
+
+    mBindery.RemoveBinding(binding); // extract binding from collision detection stuff
+    delete entry;   // which will release binding
+    return rv;
+}
+
+
+/**
+ * BindEntry()
+ *      no hash number collision -> no problem
+ *      collision
+ *          record not active -> evict, no problem
+ *          record is active
+ *              record is already doomed -> record shouldn't have been in map, no problem
+ *              record is not doomed -> doom, and replace record in map
+ *              
+ *              walk matching hashnumber list to find lowest generation number
+ *              take generation number from other (data/meta) location,
+ *                  or walk active list
+ *
+ *  NOTE: called while holding the cache service lock
+ */
+nsresult
+nsDiskCacheDevice::BindEntry(nsCacheEntry * entry)
+{
+    if (!Initialized())  return  NS_ERROR_NOT_INITIALIZED;
+    nsresult rv = NS_OK;
+    nsDiskCacheRecord record, oldRecord;
+    nsDiskCacheBinding *binding;
+    PLDHashNumber hashNumber = nsDiskCache::Hash(entry->Key()->get());
+
+    // Find out if there is already an active binding for this hash. If yes it
+    // should have another key since BindEntry() shouldn't be called twice for
+    // the same entry. Doom the old entry, the new one will get another
+    // generation number so files won't collide.
+    binding = mBindery.FindActiveBinding(hashNumber);
+    if (binding) {
+        NS_ASSERTION(!binding->mCacheEntry->Key()->Equals(*entry->Key()),
+                     "BindEntry called for already bound entry!");
+        nsCacheService::DoomEntry(binding->mCacheEntry);
+        binding = nsnull;
+    }
+
+    // Lookup hash number in cache map. There can be a colliding inactive entry.
+    // See bug #321361 comment 21 for the scenario. If there is such entry,
+    // delete it.
+    rv = mCacheMap.FindRecord(hashNumber, &record);
+    if (NS_SUCCEEDED(rv)) {
+        nsDiskCacheEntry * diskEntry = mCacheMap.ReadDiskCacheEntry(&record);
+        if (diskEntry) {
+            // compare key to be sure
+            if (!entry->Key()->Equals(diskEntry->Key())) {
+                mCacheMap.DeleteStorage(&record);
+                rv = mCacheMap.DeleteRecord(&record);
+                if (NS_FAILED(rv))  return rv;
+            }
+        }
+        record = nsDiskCacheRecord();
+    }
+
+    // create a new record for this entry
+    record.SetHashNumber(nsDiskCache::Hash(entry->Key()->get()));
+    record.SetEvictionRank(ULONG_MAX - SecondsFromPRTime(PR_Now()));
+
+    CACHE_LOG_DEBUG(("CACHE: disk BindEntry [%p %x]\n",
+        entry, record.HashNumber()));
+
+    if (!entry->IsDoomed()) {
+        // if entry isn't doomed, add it to the cache map
+        rv = mCacheMap.AddRecord(&record, &oldRecord); // deletes old record, if any
+        if (NS_FAILED(rv))  return rv;
+        
+        PRUint32    oldHashNumber = oldRecord.HashNumber();
+        if (oldHashNumber) {
+            // gotta evict this one first
+            nsDiskCacheBinding * oldBinding = mBindery.FindActiveBinding(oldHashNumber);
+            if (oldBinding) {
+                // XXX if debug : compare keys for hashNumber collision
+
+                if (!oldBinding->mCacheEntry->IsDoomed()) {
+                // we've got a live one!
+                    nsCacheService::DoomEntry(oldBinding->mCacheEntry);
+                    // storage will be delete when oldBinding->mCacheEntry is Deactivated
+                }
+            } else {
+                // delete storage
+                // XXX if debug : compare keys for hashNumber collision
+                rv = mCacheMap.DeleteStorage(&oldRecord);
+                if (NS_FAILED(rv))  return rv;  // XXX delete record we just added?
+            }
+        }
+    }
+    
+    // Make sure this entry has its associated nsDiskCacheBinding attached.
+    binding = mBindery.CreateBinding(entry, &record);
+    NS_ASSERTION(binding, "nsDiskCacheDevice::BindEntry");
+    if (!binding) return NS_ERROR_OUT_OF_MEMORY;
+    NS_ASSERTION(binding->mRecord.ValidRecord(), "bad cache map record");
+
+    return NS_OK;
+}
+
+
+/**
+ *  NOTE: called while holding the cache service lock
+ */
+void
+nsDiskCacheDevice::DoomEntry(nsCacheEntry * entry)
+{
+    CACHE_LOG_DEBUG(("CACHE: disk DoomEntry [%p]\n", entry));
+
+    nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
+    NS_ASSERTION(binding, "DoomEntry: binding == nsnull");
+    if (!binding)  return;
+
+    if (!binding->mDoomed) {
+        // so it can't be seen by FindEntry() ever again.
+#ifdef DEBUG
+        nsresult rv =
+#endif
+            mCacheMap.DeleteRecord(&binding->mRecord);
+        NS_ASSERTION(NS_SUCCEEDED(rv),"DeleteRecord failed.");
+        binding->mDoomed = PR_TRUE; // record in no longer in cache map
+    }
+}
+
+
+/**
+ *  NOTE: called while holding the cache service lock
+ */
+nsresult
+nsDiskCacheDevice::OpenInputStreamForEntry(nsCacheEntry *      entry,
+                                           nsCacheAccessMode   mode, 
+                                           PRUint32            offset,
+                                           nsIInputStream **   result)
+{
+    CACHE_LOG_DEBUG(("CACHE: disk OpenInputStreamForEntry [%p %x %u]\n",
+        entry, mode, offset));
+
+    NS_ENSURE_ARG_POINTER(entry);
+    NS_ENSURE_ARG_POINTER(result);
+
+    nsresult             rv;
+    nsDiskCacheBinding * binding = GetCacheEntryBinding(entry);
+    NS_ENSURE_TRUE(binding, NS_ERROR_UNEXPECTED);
+    
+    NS_ASSERTION(binding->mCacheEntry == entry, "binding & entry don't point to each other");
+
+    rv = binding->EnsureStreamIO();
+    if (NS_FAILED(rv)) return rv;
+
+    return binding->mStreamIO->GetInputStream(offset, result);
+}
+
+
+/**