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