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 d8dc49d5bd609668b3c4fadd6c1df12d5da20547
parent 43112 28086cf6ede84358c8d4a05be6596594a0853e5f
child 43114 01f8d7c7654d1aa8d22c5e463dd136ddc1a42b2a
push idunknown
push userunknown
push dateunknown
reviewersbiesi
bugs542222
milestone1.9.3a5pre
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;