Bug 370195 – sql device for the offline cachepatch by Dave Camp <dcamp@mozilla.com> r=jst (content part) r=biesi (rest)
authorcbiesinger@web.de
Mon, 14 May 2007 13:09:20 -0700
changeset 1426 a94492db2633c4d418f00b6a545a042a4d74747d
parent 1425 38db3a0657b8e035778ef05c9a4e30c5e45f7c3a
child 1427 3952e2a8864311582cf3fafdce8525dfa9d0c5f0
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, biesi
bugs370195
milestone1.9a5pre
Bug 370195 – sql device for the offline cachepatch by Dave Camp <dcamp@mozilla.com> r=jst (content part) r=biesi (rest)
content/base/src/Makefile.in
content/base/src/nsContentSink.cpp
content/base/src/nsContentSink.h
content/html/document/src/nsHTMLContentSink.cpp
modules/libpref/src/init/all.js
netwerk/build.mk
netwerk/cache/public/Makefile.in
netwerk/cache/public/nsIOfflineCacheSession.idl
netwerk/cache/src/Makefile.in
netwerk/cache/src/nsCacheEntry.cpp
netwerk/cache/src/nsCacheEntryDescriptor.cpp
netwerk/cache/src/nsCacheService.cpp
netwerk/cache/src/nsCacheService.h
netwerk/cache/src/nsCacheSession.cpp
netwerk/cache/src/nsCacheSession.h
netwerk/cache/src/nsDiskCacheDevice.cpp
netwerk/cache/src/nsDiskCacheDevice.h
netwerk/cache/src/nsDiskCacheDeviceSQL.cpp
netwerk/cache/src/nsDiskCacheDeviceSQL.h
netwerk/cache/src/nsDiskCacheEntry.cpp
netwerk/cache/src/nsMemoryCacheDevice.cpp
uriloader/prefetch/nsPrefetchService.cpp
uriloader/prefetch/nsPrefetchService.h
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -54,16 +54,17 @@ REQUIRES	= xpcom \
 		  layout \
 		  widget \
 		  view \
 		  locale \
 		  htmlparser \
 		  js \
 		  webshell \
 		  necko \
+		  nkcache \
 		  mimetype \
 		  caps \
 		  lwbrk \
 		  uconv \
 		  docshell \
 		  pref \
 		  xpconnect \
 		  unicharutil \
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -63,16 +63,20 @@
 #include "nsIViewManager.h"
 #include "nsIContentViewer.h"
 #include "nsIAtom.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMWindowInternal.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsNetCID.h"
+#include "nsICache.h"
+#include "nsICacheService.h"
+#include "nsICacheSession.h"
+#include "nsIOfflineCacheSession.h"
 #include "nsICookieService.h"
 #include "nsIPrompt.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsParserUtils.h"
 #include "nsCRT.h"
 #include "nsEscape.h"
 #include "nsWeakReference.h"
@@ -668,17 +672,19 @@ nsContentSink::ProcessLink(nsIContent* a
   PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
   // prefetch href if relation is "next" or "prefetch"
   if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
     PrefetchHref(aHref, hasPrefetch, PR_FALSE);
   }
 
   // fetch href into the offline cache if relation is "offline-resource"
   if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
-    PrefetchHref(aHref, PR_TRUE, PR_TRUE);
+    AddOfflineResource(aHref);
+    if (mSaveOfflineResources)
+      PrefetchHref(aHref, PR_TRUE, PR_TRUE);
   }
 
   // is it a stylesheet link?
   if (linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet")) == -1) {
     return NS_OK;
   }
 
   PRBool isAlternate = linkTypes.IndexOf(NS_LITERAL_STRING("alternate")) != -1;
@@ -804,16 +810,125 @@ nsContentSink::PrefetchHref(const nsAStr
       if (aOffline)
         prefetchService->PrefetchURIForOfflineUse(uri, mDocumentURI, aExplicit);
       else
         prefetchService->PrefetchURI(uri, mDocumentURI, aExplicit);
     }
   }
 }
 
+nsresult
+nsContentSink::GetOfflineCacheSession(nsIOfflineCacheSession **aSession)
+{
+  if (!mOfflineCacheSession) {
+    nsresult rv;
+    nsCOMPtr<nsICacheService> serv =
+      do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsICacheSession> session;
+    rv = serv->CreateSession("HTTP-offline",
+                             nsICache::STORE_OFFLINE,
+                             nsICache::STREAM_BASED,
+                             getter_AddRefs(session));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mOfflineCacheSession =
+      do_QueryInterface(session, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  NS_ADDREF(*aSession = mOfflineCacheSession);
+
+  return NS_OK;
+}
+
+nsresult
+nsContentSink::AddOfflineResource(const nsAString &aHref)
+{
+  PRBool match;
+  nsresult rv;
+
+  nsCAutoString ownerHost;
+  rv = mDocumentURI->GetHostPort(ownerHost);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString ownerSpec;
+  rv = mDocumentURI->GetSpec(ownerSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mHaveOfflineResources) {
+    mHaveOfflineResources = PR_TRUE;
+    mSaveOfflineResources = PR_FALSE;
+
+    // only let http and https urls add offline resources
+    nsresult rv = mDocumentURI->SchemeIs("http", &match);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!match) {
+      rv = mDocumentURI->SchemeIs("https", &match);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (!match)
+        return NS_OK;
+    }
+
+    nsCOMPtr<nsIOfflineCacheSession> session;
+    rv = GetOfflineCacheSession(getter_AddRefs(session));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // we're going to replace the list, clear it out
+    rv = session->SetOwnedKeys(ownerHost, ownerSpec, 0, nsnull);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mSaveOfflineResources = PR_TRUE;
+  }
+
+  if (!mSaveOfflineResources) return NS_OK;
+
+  const nsACString &charset = mDocument->GetDocumentCharacterSet();
+  nsCOMPtr<nsIURI> uri;
+  rv = NS_NewURI(getter_AddRefs(uri), aHref,
+                 charset.IsEmpty() ? nsnull : PromiseFlatCString(charset).get(),
+                 mDocumentBaseURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // only http and https urls can be marked as offline resources
+  rv = uri->SchemeIs("http", &match);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!match) {
+    rv = uri->SchemeIs("https", &match);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!match)
+      return NS_OK;
+  }
+
+  nsCAutoString spec;
+  rv = uri->GetSpec(spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIOfflineCacheSession> offlineCacheSession;
+  rv = GetOfflineCacheSession(getter_AddRefs(offlineCacheSession));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // url fragments aren't used in cache keys
+  nsCAutoString::const_iterator specStart, specEnd;
+  spec.BeginReading(specStart);
+  spec.EndReading(specEnd);
+  if (FindCharInReadable('#', specStart, specEnd)) {
+    spec.BeginReading(specEnd);
+    offlineCacheSession->AddOwnedKey(ownerHost, ownerSpec,
+                                     Substring(specEnd, specStart));
+  } else {
+    offlineCacheSession->AddOwnedKey(ownerHost, ownerSpec, spec);
+  }
+
+  return NS_OK;
+}
+
 void
 nsContentSink::ScrollToRef()
 {
   if (mRef.IsEmpty()) {
     return;
   }
 
   PRBool didScroll = PR_FALSE;
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -70,16 +70,17 @@ class nsIDocShell;
 class nsICSSLoader;
 class nsIParser;
 class nsIAtom;
 class nsIChannel;
 class nsIContent;
 class nsIViewManager;
 class nsNodeInfoManager;
 class nsScriptLoader;
+class nsIOfflineCacheSession;
 
 #ifdef NS_DEBUG
 
 extern PRLogModuleInfo* gContentSinkLogModuleInfo;
 
 #define SINK_TRACE_CALLS              0x1
 #define SINK_TRACE_REFLOW             0x2
 #define SINK_ALWAYS_REFLOW            0x4
@@ -162,16 +163,18 @@ protected:
   virtual nsresult ProcessStyleLink(nsIContent* aElement,
                                     const nsSubstring& aHref,
                                     PRBool aAlternate,
                                     const nsSubstring& aTitle,
                                     const nsSubstring& aType,
                                     const nsSubstring& aMedia);
 
   void PrefetchHref(const nsAString &aHref, PRBool aExplicit, PRBool aOffline);
+  nsresult GetOfflineCacheSession(nsIOfflineCacheSession **aSession);
+  nsresult AddOfflineResource(const nsAString &aHref);
 
   void ScrollToRef();
   nsresult RefreshIfEnabled(nsIViewManager* vm);
 
   // Start layout.  If aIgnorePendingSheets is true, this will happen even if
   // we still have stylesheet loads pending.  Otherwise, we'll wait until the
   // stylesheets are all done loading.
   void StartLayout(PRBool aIgnorePendingSheets);
@@ -247,29 +250,36 @@ protected:
   // frequency parser interrupt mode without falling through to the
   // logic which decides whether to switch to the high frequency
   // parser interrupt mode.
   PRUint8 mDeflectedCount;
 
   // Do we notify based on time?
   PRPackedBool mNotifyOnTimer;
 
+  // For saving <link rel="offline-resource"> links
+  nsCOMPtr<nsIOfflineCacheSession> mOfflineCacheSession;
+
   // Have we already called BeginUpdate for this set of content changes?
   PRUint8 mBeganUpdate : 1;
   PRUint8 mLayoutStarted : 1;
   PRUint8 mScrolledToRefAlready : 1;
   PRUint8 mCanInterruptParser : 1;
   PRUint8 mDynamicLowerValue : 1;
   PRUint8 mParsing : 1;
   PRUint8 mDroppedTimer : 1;
   PRUint8 mInTitle : 1;
   PRUint8 mChangeScrollPosWhenScrollingToRef : 1;
   // If true, we deferred starting layout until sheets load
   PRUint8 mDeferredLayoutStart : 1;
-  
+  // true if an <link rel="offline-resource"> nodes have been encountered.
+  PRUint8 mHaveOfflineResources : 1;
+  // true if offline-resource links should be saved to the offline cache
+  PRUint8 mSaveOfflineResources : 1;
+
   // -- Can interrupt parsing members --
   PRUint32 mDelayTimerStart;
 
   // Interrupt parsing during token procesing after # of microseconds
   PRInt32 mMaxTokenProcessingTime;
 
   // Switch between intervals when time is exceeded
   PRInt32 mDynamicIntervalSwitchThreshold;
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -3007,17 +3007,19 @@ HTMLContentSink::ProcessLINKTag(const ns
           if (!hrefVal.IsEmpty()) {
             PrefetchHref(hrefVal, hasPrefetch, PR_FALSE);
           }
         }
         if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
           nsAutoString hrefVal;
           element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
           if (!hrefVal.IsEmpty()) {
-            PrefetchHref(hrefVal, PR_TRUE, PR_TRUE);
+            AddOfflineResource(hrefVal);
+            if (mSaveOfflineResources)
+              PrefetchHref(hrefVal, PR_TRUE, PR_TRUE);
           }
         }
       }
     }
   }
 
   return result;
 }
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -65,17 +65,17 @@ pref("browser.cache.memory.enable",     
 //pref("browser.cache.memory.capacity",     -1);
 // -1 = determine dynamically, 0 = none, n = memory capacity in kilobytes
 pref("browser.cache.disk_cache_ssl",        false);
 // 0 = once-per-session, 1 = each-time, 2 = never, 3 = when-appropriate/automatically
 pref("browser.cache.check_doc_frequency",   3);
 
 pref("browser.cache.offline.enable",           true);
 // offline cache capacity in kilobytes
-pref("browser.cache.offline.capacity",         51200);
+pref("browser.cache.offline.capacity",         10240);
 
 // Fastback caching - if this pref is negative, then we calculate the number
 // of content viewers to cache based on the amount of available memory.
 pref("browser.sessionhistory.max_total_viewers", -1);
 
 pref("browser.display.use_document_fonts",  1);  // 0 = never, 1 = quick, 2 = always
 pref("browser.display.use_document_colors", true);
 pref("browser.display.use_system_colors",   false);
--- a/netwerk/build.mk
+++ b/netwerk/build.mk
@@ -53,16 +53,21 @@ TIERS += zlib \
 ifndef MOZ_NATIVE_ZLIB
 tier_zlib_dirs	+= modules/zlib
 endif
 
 #
 # tier "necko" - the networking library and its dependencies
 #
 
+# the offline cache uses mozStorage
+ifdef MOZ_STORAGE
+tier_necko_dirs += storage/public
+endif
+
 # these are only in the necko tier because libpref needs it
 
 ifneq (1_,$(MOZ_NO_XPCOM_OBSOLETE)_$(MOZ_XPINSTALL))
 tier_necko_dirs += modules/libreg
 endif
 
 ifndef MOZ_NO_XPCOM_OBSOLETE
 tier_necko_dirs += xpcom/obsolete
--- a/netwerk/cache/public/Makefile.in
+++ b/netwerk/cache/public/Makefile.in
@@ -48,13 +48,14 @@ include $(DEPTH)/config/autoconf.mk
 
 XPIDLSRCS	= \
 		nsICache.idl		      \
 		nsICacheEntryDescriptor.idl   \
 		nsICacheListener.idl	      \
 		nsICacheService.idl           \
 		nsICacheSession.idl           \
 		nsICacheVisitor.idl           \
-	        $(NULL)
+		nsIOfflineCacheSession.idl    \
+		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -DIMPL_NS_NET
new file mode 100644
--- /dev/null
+++ b/netwerk/cache/public/nsIOfflineCacheSession.idl
@@ -0,0 +1,160 @@
+/* -*- Mode: IDL; 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 nsIOfflineCacheSession.idl.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.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 "nsISupports.idl"
+#include "nsICache.idl"
+
+[scriptable, uuid(e9581d9f-3c0c-4722-8d2b-3d18f8d41299)]
+interface nsIOfflineCacheSession : nsISupports
+{
+    /**
+     * The offline cache is meant to reliably store resources for
+     * offline use.  The expected semantics are:
+     *
+     * a) Once populated, the cache will not evict an application resource
+     *    unless explicitly asked.
+     *
+     * b) Resources no longer in use by the application should be evicted.
+     *
+     * c) If the cache fills up, new entries should be rejected rather
+     *    than throwing out old ones.
+     *
+     * The offline cache uses domains to concretely represent an
+     * application.  It maintains a list of resources to be pinned for
+     * each domain.  This list is separate from actual cache
+     * population - the caller is still responsible for placing items
+     * in the cache, and ownership can be declared without a
+     * corresponding entry.
+     *
+     * A key can optionally be associated with a specific URI within
+     * the domain.
+     */
+
+    /**
+     * Sets the resources owned by a given domain/URI pair.
+     *
+     * Setting a list will remove any resources previously owned by this
+     * domain/URI pair.
+     *
+     * A key can be added while there is no associated entry.  When
+     * an entry is created with this key, it will be owned by the
+     * domain/URI pair.
+     *
+     * @param ownerDomain The domain that owns the resources.
+     * @param ownerURI The specific URI that owns the resources.  This can
+     *        be empty if no URI specifically owns the resources.
+     * @param count The number of keys in keys.
+     * @param keys The keys that the domain/URI pair own.  This can be empty
+     *        to clear ownership for the domain/URI pair.
+     */
+    void setOwnedKeys(in ACString ownerDomain,
+                      in ACString ownerURI,
+                      in unsigned long count,
+                      [array, size_is(count)]in string keys);
+
+    /**
+     * Gets the list of resources owned by a given domain/URI pair.
+     *
+     * @param ownerDomain The domain that owns the resources.
+     * @param ownerURI The specific URI that owns the resources.  This can
+     *        be empty if no URI specifically owns the resources.
+     * @param count The number of keys in keys.
+     * @param keys The keys that the domain/URI pair own.
+     */
+    void getOwnedKeys(in ACString ownerDomain,
+                      in ACString ownerURI,
+                      out unsigned long count,
+                      [array, size_is(count)]out string keys);
+
+    /**
+     * Adds an owned key to a domain/URI pair.
+     *
+     * A key can be added while there is no associated entry.  When
+     * an entry is created with this key, it will be owned by the
+     * domain/URI pair.
+     *
+     * @param ownerDomain The domain that owns the resources.
+     * @param ownerURI The specific URI that owns the resources.  This can
+     *        be empty if no URI specifically owns the resources.
+     * @param key The key to add.
+     */
+    void addOwnedKey(in ACString ownerDomain,
+                     in ACString ownerURI,
+                     in ACString key);
+
+    /**
+     * Removes an owned key from a domain/URI pair.
+     *
+     * If the key does not exist, an NS_ERROR_NOT_AVAILABLE exception
+     * will be thrown.
+     *
+     * @param ownerDomain The domain that owns the resources.
+     * @param ownerURI The specific URI that owns the resources.  This can
+     *        be empty if no URI specifically owns the resources.
+     * @param key The key to remove.
+     */
+    void removeOwnedKey(in ACString ownerDomain,
+                        in ACString ownerURI,
+                        in ACString key);
+
+    /**
+     * Checks whether a key is owned by a given domain/URI pair.
+     *
+     * @param ownerDomain The domain that owns the resources.
+     * @param ownerURI The specific URI that owns the resources.  This can
+     *        be empty if no URI specifically owns the resources.
+     * @param key The key to check
+     */
+    boolean keyIsOwned(in ACString ownerDomain,
+                       in ACString ownerURI,
+                       in ACString key);
+
+    /**
+     * Remove all keys owned by a domain, including keys owned by
+     * a specific URI.
+     *
+     * @param domain The domain for which keys should be removed.
+     */
+    void clearKeysOwnedByDomain(in ACString ownerDomain);
+
+    /**
+     * Evict all entries that are not owned by a domain.
+     */
+    void evictUnownedEntries();
+};
--- a/netwerk/cache/src/Makefile.in
+++ b/netwerk/cache/src/Makefile.in
@@ -64,33 +64,34 @@ CPPSRCS		= \
 	nsCacheEntryDescriptor.cpp \
 	nsCacheMetaData.cpp \
 	nsCacheService.cpp \
 	nsCacheSession.cpp \
 	nsMemoryCacheDevice.cpp \
 	$(NULL)
 
 ifdef NECKO_DISK_CACHE
-ifdef NECKO_DISK_CACHE_SQL
-DEFINES		+= -DNECKO_DISK_CACHE_SQL
-REQUIRES	+= storage
-CPPSRCS += \
-	nsDiskCacheDeviceSQL.cpp \
-	$(NULL)
-else
 CPPSRCS += \
 	nsDiskCacheBinding.cpp \
 	nsDiskCacheBlockFile.cpp \
 	nsDiskCacheDevice.cpp \
 	nsDiskCacheEntry.cpp \
 	nsDiskCacheMap.cpp \
 	nsDiskCacheStreams.cpp \
 	nsDeleteDir.cpp \
 	$(NULL)
 endif
+
+ifdef MOZ_STORAGE
+CPPSRCS += \
+	nsDiskCacheDeviceSQL.cpp \
+	$(NULL)
+
+REQUIRES += storage
+DEFINES += -DNECKO_OFFLINE_CACHE
 endif
 
 LOCAL_INCLUDES=-I$(srcdir)/../../base/src
 
 include $(topsrcdir)/config/config.mk
 
 
 include $(topsrcdir)/config/rules.mk
--- a/netwerk/cache/src/nsCacheEntry.cpp
+++ b/netwerk/cache/src/nsCacheEntry.cpp
@@ -302,18 +302,18 @@ nsCacheEntryInfo::GetClientID(char ** cl
 }
 
 
 NS_IMETHODIMP
 nsCacheEntryInfo::GetDeviceID(char ** deviceID)
 {
     NS_ENSURE_ARG_POINTER(deviceID);
     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
-    
-    *deviceID = nsCRT::strdup(mCacheEntry->GetDeviceID());
+
+    *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;
--- a/netwerk/cache/src/nsCacheEntryDescriptor.cpp
+++ b/netwerk/cache/src/nsCacheEntryDescriptor.cpp
@@ -90,17 +90,17 @@ nsCacheEntryDescriptor::GetClientID(char
 
 NS_IMETHODIMP
 nsCacheEntryDescriptor::GetDeviceID(char ** result)
 {
     NS_ENSURE_ARG_POINTER(result);
     nsCacheServiceAutoLock lock;
     if (!mCacheEntry)  return NS_ERROR_NOT_AVAILABLE;
 
-    *result = nsCRT::strdup(mCacheEntry->GetDeviceID());
+    *result = NS_strdup(mCacheEntry->GetDeviceID());
     return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 
 NS_IMETHODIMP
 nsCacheEntryDescriptor::GetKey(nsACString &result)
 {
     nsCacheServiceAutoLock lock;
@@ -433,17 +433,17 @@ nsCacheEntryDescriptor::GetMetaDataEleme
     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 = PL_strdup(value);
+    *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)
--- a/netwerk/cache/src/nsCacheService.cpp
+++ b/netwerk/cache/src/nsCacheService.cpp
@@ -43,21 +43,20 @@
 #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_DISK_CACHE_SQL
+#ifdef NECKO_OFFLINE_CACHE
 #include "nsDiskCacheDeviceSQL.h"
-#else
-#include "nsDiskCacheDevice.h"
 #endif
 
 #include "nsIObserverService.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefBranch2.h"
 #include "nsILocalFile.h"
 #include "nsIOService.h"
@@ -81,17 +80,17 @@
 #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      51200
+#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"
 
 
 class nsCacheProfilePrefObserver : public nsIObserver
 {
@@ -657,24 +656,26 @@ nsCacheService::Shutdown()
 
         // 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
         delete mOfflineDevice;
         mOfflineDevice = nsnull;
+#endif // !NECKO_OFFLINE_CACHE
 
-#if defined(PR_LOGGING)
+#if defined(NECKO_DISK_CACHE) && defined(PR_LOGGING)
         LogCacheStatistics();
 #endif
-#endif // !NECKO_DISK_CACHE
     }
 }
 
 
 NS_METHOD
 nsCacheService::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
 {
     nsresult  rv;
@@ -760,29 +761,31 @@ nsCacheService::EvictEntriesForClient(co
             if (!mDiskDevice) {
                 rv = CreateDiskDevice();
                 if (NS_FAILED(rv)) return rv;
             }
             rv = mDiskDevice->EvictEntries(clientID);
             if (NS_FAILED(rv)) return rv;
         }
     }
+#endif // ! NECKO_DISK_CACHE
 
+#ifdef NECKO_OFFLINE_CACHE
     if (storagePolicy == nsICache::STORE_ANYWHERE ||
         storagePolicy == nsICache::STORE_OFFLINE) {
         if (mEnableOfflineDevice) {
             if (!mOfflineDevice) {
                 rv = CreateOfflineDevice();
                 if (NS_FAILED(rv)) return rv;
             }
             rv = mOfflineDevice->EvictEntries(clientID);
             if (NS_FAILED(rv)) return rv;
         }
     }
-#endif // ! NECKO_DISK_CACHE
+#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) {
             rv = mMemoryDevice->EvictEntries(clientID);
             if (NS_FAILED(rv)) return rv;
@@ -822,16 +825,172 @@ nsCacheService::IsStorageEnabledForPolic
     if (gService->mEnableOfflineDevice &&
         storagePolicy == nsICache::STORE_OFFLINE) {
         return PR_TRUE;
     }
     
     return PR_FALSE;
 }
 
+nsresult
+nsCacheService::SetOfflineOwnedKeys(nsCacheSession * session,
+                                    const nsACString & ownerDomain,
+                                    const nsACString & ownerURI,
+                                    PRUint32 count,
+                                    const char ** keys)
+{
+#ifdef NECKO_OFFLINE_CACHE
+    if (session->StoragePolicy() != nsICache::STORE_OFFLINE)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    if (!gService->mOfflineDevice) {
+        nsresult rv = gService->CreateOfflineDevice();
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return gService->mOfflineDevice->SetOwnedKeys(session->ClientID()->get(),
+                                                  ownerDomain,
+                                                  ownerURI,
+                                                  count,
+                                                  keys);
+#else // !NECKO_OFFLINE_CACHE
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult nsCacheService::GetOfflineOwnedKeys(nsCacheSession * session,
+                                             const nsACString & ownerDomain,
+                                             const nsACString & ownerURI,
+                                             PRUint32 * count,
+                                             char *** keys)
+{
+#ifdef NECKO_OFFLINE_CACHE
+    if (session->StoragePolicy() != nsICache::STORE_OFFLINE)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    if (!gService->mOfflineDevice) {
+        nsresult rv = gService->CreateOfflineDevice();
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return gService->mOfflineDevice->GetOwnedKeys(session->ClientID()->get(),
+                                                  ownerDomain,
+                                                  ownerURI,
+                                                  count,
+                                                  keys);
+#else // !NECKO_OFFLINE_CACHE
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult nsCacheService::AddOfflineOwnedKey(nsCacheSession * session,
+                                            const nsACString & ownerDomain,
+                                            const nsACString & ownerURI,
+                                            const nsACString & key)
+{
+#ifdef NECKO_OFFLINE_CACHE
+    if (session->StoragePolicy() != nsICache::STORE_OFFLINE)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    if (!gService->mOfflineDevice) {
+        nsresult rv = gService->CreateOfflineDevice();
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return gService->mOfflineDevice->AddOwnedKey(session->ClientID()->get(),
+                                                 ownerDomain,
+                                                 ownerURI,
+                                                 key);
+#else // !NECKO_OFFLINE_CACHE
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult nsCacheService::RemoveOfflineOwnedKey(nsCacheSession * session,
+                                               const nsACString & ownerDomain,
+                                               const nsACString & ownerURI,
+                                               const nsACString & key)
+{
+#ifdef NECKO_OFFLINE_CACHE
+    if (session->StoragePolicy() != nsICache::STORE_OFFLINE)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    if (!gService->mOfflineDevice) {
+        nsresult rv = gService->CreateOfflineDevice();
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return gService->mOfflineDevice->RemoveOwnedKey(session->ClientID()->get(),
+                                                    ownerDomain,
+                                                    ownerURI,
+                                                    key);
+#else // !NECKO_OFFLINE_CACHE
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult nsCacheService::OfflineKeyIsOwned(nsCacheSession * session,
+                                           const nsACString &  ownerDomain,
+                                           const nsACString & ownerURI,
+                                           const nsACString & key,
+                                           PRBool *isOwned)
+{
+#ifdef NECKO_OFFLINE_CACHE
+    if (session->StoragePolicy() != nsICache::STORE_OFFLINE)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    if (!gService->mOfflineDevice) {
+        nsresult rv = gService->CreateOfflineDevice();
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return gService->mOfflineDevice->KeyIsOwned(session->ClientID()->get(),
+                                                ownerDomain,
+                                                ownerURI,
+                                                key,
+                                                isOwned);
+#else // !NECKO_OFFLINE_CACHE
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult nsCacheService::ClearOfflineKeysOwnedByDomain(nsCacheSession * session,
+                                                       const nsACString & domain)
+{
+#ifdef NECKO_OFFLINE_CACHE
+    if (session->StoragePolicy() != nsICache::STORE_OFFLINE)
+        return NS_ERROR_NOT_AVAILABLE;
+    if (!gService->mOfflineDevice) {
+        nsresult rv = gService->CreateOfflineDevice();
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return gService->mOfflineDevice->ClearKeysOwnedByDomain(session->ClientID()->get(),
+                                                            domain);
+#else // !NECKO_OFFLINE_CACHE
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult nsCacheService::EvictUnownedOfflineEntries(nsCacheSession * session)
+{
+#ifdef NECKO_OFFLINE_CACHE
+    if (session->StoragePolicy() != nsICache::STORE_OFFLINE)
+        return NS_ERROR_NOT_AVAILABLE;
+
+    if (!gService->mOfflineDevice) {
+        nsresult rv = gService->CreateOfflineDevice();
+        if (NS_FAILED(rv)) return rv;
+    }
+
+    return gService->mOfflineDevice->EvictUnownedEntries(session->ClientID()->get());
+#else // !NECKO_OFFLINE_CACHE
+    return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
 
 NS_IMETHODIMP nsCacheService::VisitEntries(nsICacheVisitor *visitor)
 {
     NS_ENSURE_ARG_POINTER(visitor);
 
     nsCacheServiceAutoLock lock;
 
     if (!(mEnableDiskDevice || mEnableMemoryDevice))
@@ -851,26 +1010,28 @@ NS_IMETHODIMP nsCacheService::VisitEntri
     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_DISK_CACHE
+#endif // !NECKO_OFFLINE_CACHE
 
     // XXX notify any shutdown process that visitation is complete for THIS visitor.
     // XXX keep queue of visitors
 
     return NS_OK;
 }
 
 
@@ -914,29 +1075,28 @@ nsCacheService::CreateDiskDevice()
     NS_NOTREACHED("nsCacheService::CreateDiskDevice");
     return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
 nsresult
 nsCacheService::CreateOfflineDevice()
 {
-#ifdef NECKO_DISK_CACHE
+#ifdef NECKO_OFFLINE_CACHE
     CACHE_LOG_ALWAYS(("Creating offline device"));
 
-    // XXX: want a sql-based device
     if (!mEnableOfflineDevice) return NS_ERROR_NOT_AVAILABLE;
     if (mOfflineDevice)        return NS_OK;
 
-    mOfflineDevice = new nsDiskCacheDevice;
+    mOfflineDevice = new nsOfflineCacheDevice;
     if (!mOfflineDevice)       return NS_ERROR_OUT_OF_MEMORY;
 
     // set the preferences
-    mOfflineDevice->SetCacheParentDirectoryAndName(mObserver->OfflineCacheParentDirectory(),
-                                                   NS_LITERAL_CSTRING("OfflineCache"));
+    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;
@@ -1277,27 +1437,27 @@ nsCacheService::SearchCacheDevices(nsCSt
         }
 #endif // !NECKO_DISK_CACHE
     }
 
     if (!entry && (policy == nsICache::STORE_OFFLINE ||
                    (policy == nsICache::STORE_ANYWHERE &&
                     gIOService->IsOffline()))) {
 
-#ifdef NECKO_DISK_CACHE
+#ifdef NECKO_OFFLINE_CACHE
         if (mEnableOfflineDevice) {
             if (!mOfflineDevice) {
                 nsresult rv = CreateOfflineDevice();
                 if (NS_FAILED(rv))
                     return nsnull;
             }
 
             entry = mOfflineDevice->FindEntry(key, collision);
         }
-#endif
+#endif // !NECKO_OFFLINE_CACHE
     }
 
     return entry;
 }
 
 
 nsCacheDevice *
 nsCacheService::EnsureEntryHasDevice(nsCacheEntry * entry)
@@ -1331,32 +1491,32 @@ nsCacheService::EnsureEntryHasDevice(nsC
             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_DISK_CACHE
+#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_DISK_CACHE
+#endif // ! NECKO_OFFLINE_CACHE
 
     if (device) 
         entry->SetCacheDevice(device);
     return device;
 }
 
 
 nsresult
@@ -1411,25 +1571,27 @@ nsCacheService::OnProfileShutdown(PRBool
 #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_DISK_CACHE
+#endif // !NECKO_OFFLINE_CACHE
 
     if (gService->mMemoryDevice) {
         // clear memory cache
         gService->mMemoryDevice->EvictEntries(nsnull);
     }
 
 }
 
@@ -1453,30 +1615,32 @@ nsCacheService::OnProfileChanged()
         // 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_DISK_CACHE
+#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
             gService->mMemoryDevice->SetCapacity(gService->mObserver->MemoryCacheCapacity());
         } else {
             // tell memory device to evict everything
@@ -1520,21 +1684,21 @@ nsCacheService::SetOfflineCacheEnabled(P
 }
 
 void
 nsCacheService::SetOfflineCacheCapacity(PRInt32  capacity)
 {
     if (!gService)  return;
     nsCacheServiceAutoLock lock;
 
-#ifdef NECKO_DISK_CACHE
+#ifdef NECKO_OFFLINE_CACHE
     if (gService->mOfflineDevice) {
         gService->mOfflineDevice->SetCapacity(capacity);
     }
-#endif // !NECKO_DISK_CACHE
+#endif // !NECKO_OFFLINE_CACHE
 
     gService->mEnableOfflineDevice = gService->mObserver->OfflineCacheEnabled();
 }
 
 
 void
 nsCacheService::SetMemoryCache()
 {
--- a/netwerk/cache/src/nsCacheService.h
+++ b/netwerk/cache/src/nsCacheService.h
@@ -55,16 +55,17 @@
 #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
@@ -91,16 +92,50 @@ public:
                                     nsICacheListener *         listener,
                                     nsICacheEntryDescriptor ** result);
 
     static nsresult  EvictEntriesForSession(nsCacheSession *   session);
 
     static nsresult  IsStorageEnabledForPolicy(nsCacheStoragePolicy  storagePolicy,
                                                PRBool *              result);
 
+
+    static nsresult  SetOfflineOwnedKeys(nsCacheSession *             session,
+                                         const nsACString &           ownerDomain,
+                                         const nsACString &           ownerUri,
+                                         PRUint32                     count,
+                                         const char **                keys);
+
+    static nsresult  GetOfflineOwnedKeys(nsCacheSession *             session,
+                                         const nsACString &           ownerDomain,
+                                         const nsACString &           ownerURI,
+                                         PRUint32 *                   count,
+                                         char ***                     keys);
+
+    static nsresult  AddOfflineOwnedKey(nsCacheSession *              session,
+                                        const nsACString &            ownerDomain,
+                                        const nsACString &            ownerURI,
+                                        const nsACString &            key);
+
+    static nsresult  RemoveOfflineOwnedKey(nsCacheSession *           session,
+                                           const nsACString &         ownerDomain,
+                                           const nsACString &         ownerURI,
+                                           const nsACString &         key);
+
+    static nsresult  OfflineKeyIsOwned(nsCacheSession *               session,
+                                       const nsACString &             ownerDomain,
+                                       const nsACString &             ownerURI,
+                                       const nsACString &             key,
+                                       PRBool *                       isOwned);
+
+    static nsresult  ClearOfflineKeysOwnedByDomain(nsCacheSession   * session,
+                                                   const nsACString & domain);
+
+    static nsresult  EvictUnownedOfflineEntries(nsCacheSession *      session);
+
     /**
      * Methods called by nsCacheEntryDescriptor
      */
 
     static void      CloseDescriptor(nsCacheEntryDescriptor * descriptor);
 
     static nsresult  GetFileForEntry(nsCacheEntry *         entry,
                                      nsIFile **             result);
@@ -243,17 +278,17 @@ private:
     PRBool                          mInitialized;
     
     PRBool                          mEnableMemoryDevice;
     PRBool                          mEnableDiskDevice;
     PRBool                          mEnableOfflineDevice;
 
     nsMemoryCacheDevice *           mMemoryDevice;
     nsDiskCacheDevice *             mDiskDevice;
-    nsDiskCacheDevice *             mOfflineDevice;
+    nsOfflineCacheDevice *          mOfflineDevice;
 
     nsCacheEntryHashTable           mActiveEntries;
     PRCList                         mDoomedEntries;
 
     // stats
     
     PRUint32                        mTotalEntries;
     PRUint32                        mCacheHits;
--- a/netwerk/cache/src/nsCacheSession.cpp
+++ b/netwerk/cache/src/nsCacheSession.cpp
@@ -37,19 +37,27 @@
  * 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_ADDREF(nsCacheSession)
+NS_IMPL_RELEASE(nsCacheSession)
 
-NS_IMPL_ISUPPORTS1(nsCacheSession, nsICacheSession)
+NS_INTERFACE_MAP_BEGIN(nsCacheSession)
+    NS_INTERFACE_MAP_ENTRY(nsICacheSession)
+    NS_INTERFACE_MAP_ENTRY_CONDITIONAL(
+        nsIOfflineCacheSession, (StoragePolicy() == nsICache::STORE_OFFLINE))
+    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICacheSession)
+NS_INTERFACE_MAP_END
 
 nsCacheSession::nsCacheSession(const char *         clientID,
                                nsCacheStoragePolicy storagePolicy,
                                PRBool               streamBased)
     : mClientID(clientID),
       mInfo(0)
 {
   SetStoragePolicy(storagePolicy);
@@ -123,8 +131,56 @@ NS_IMETHODIMP nsCacheSession::EvictEntri
 
 
 NS_IMETHODIMP nsCacheSession::IsStorageEnabled(PRBool *result)
 {
 
     return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result);
 }
 
+NS_IMETHODIMP nsCacheSession::SetOwnedKeys(const nsACString & domain,
+                                           const nsACString & uri,
+                                           PRUint32 count,
+                                           const char ** keys)
+{
+    return nsCacheService::SetOfflineOwnedKeys(this, domain, uri, count, keys);
+}
+
+NS_IMETHODIMP nsCacheSession::GetOwnedKeys(const nsACString & domain,
+                                           const nsACString & uri,
+                                           PRUint32 * count,
+                                           char *** keys)
+{
+    return nsCacheService::GetOfflineOwnedKeys(this, domain, uri, count, keys);
+}
+
+NS_IMETHODIMP nsCacheSession::AddOwnedKey(const nsACString & domain,
+                                          const nsACString & uri,
+                                          const nsACString & key)
+{
+    return nsCacheService::AddOfflineOwnedKey(this, domain, uri, key);
+}
+
+NS_IMETHODIMP nsCacheSession::RemoveOwnedKey(const nsACString & domain,
+                                             const nsACString & uri,
+                                             const nsACString & key)
+{
+    return nsCacheService::RemoveOfflineOwnedKey(this, domain, uri, key);
+}
+
+NS_IMETHODIMP nsCacheSession::KeyIsOwned(const nsACString & domain,
+                                         const nsACString & uri,
+                                         const nsACString & key,
+                                         PRBool * isOwned)
+{
+    return nsCacheService::OfflineKeyIsOwned(this, domain, uri, key, isOwned);
+}
+
+NS_IMETHODIMP nsCacheSession::ClearKeysOwnedByDomain(const nsACString & domain)
+{
+    return nsCacheService::ClearOfflineKeysOwnedByDomain(this, domain);
+}
+
+NS_IMETHODIMP nsCacheSession::EvictUnownedEntries()
+{
+    return nsCacheService::EvictUnownedOfflineEntries(this);
+}
+
--- a/netwerk/cache/src/nsCacheSession.h
+++ b/netwerk/cache/src/nsCacheSession.h
@@ -41,23 +41,26 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsCacheSession_h_
 #define _nsCacheSession_h_
 
 #include "nspr.h"
 #include "nsError.h"
 #include "nsICacheSession.h"
+#include "nsIOfflineCacheSession.h"
 #include "nsString.h"
 
 class nsCacheSession : public nsICacheSession
+                     , public nsIOfflineCacheSession
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSICACHESESSION
+    NS_DECL_NSIOFFLINECACHESESSION
     
     nsCacheSession(const char * clientID, nsCacheStoragePolicy storagePolicy, PRBool streamBased);
     virtual ~nsCacheSession();
     
     nsCString *           ClientID()      { return &mClientID; }
 
     enum SessionInfo {
         eStoragePolicyMask        = 0x000000FF,
--- a/netwerk/cache/src/nsDiskCacheDevice.cpp
+++ b/netwerk/cache/src/nsDiskCacheDevice.cpp
@@ -175,17 +175,17 @@ private:
 };
 
 NS_IMPL_ISUPPORTS1(nsDiskCacheDeviceInfo, nsICacheDeviceInfo)
 
 /* readonly attribute string description; */
 NS_IMETHODIMP nsDiskCacheDeviceInfo::GetDescription(char ** aDescription)
 {
     NS_ENSURE_ARG_POINTER(aDescription);
-    *aDescription = nsCRT::strdup("Disk cache device");
+    *aDescription = NS_strdup("Disk cache device");
     return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 /* readonly attribute string usageReport; */
 NS_IMETHODIMP nsDiskCacheDeviceInfo::GetUsageReport(char ** usageReport)
 {
     NS_ENSURE_ARG_POINTER(usageReport);
     nsCString buffer;
@@ -924,53 +924,16 @@ nsDiskCacheDevice::SetCacheParentDirecto
     rv = parentDir->Clone(getter_AddRefs(directory));
     if (NS_FAILED(rv))  return;
     rv = directory->AppendNative(NS_LITERAL_CSTRING("Cache"));
     if (NS_FAILED(rv))  return;
     
     mCacheDirectory = do_QueryInterface(directory);
 }
 
-// XXX: This is here to support the offline cache, and can be removed
-// XXX: once it has its own cache implementation
-void
-nsDiskCacheDevice::SetCacheParentDirectoryAndName(nsILocalFile * parentDir,
-                                                  const nsACString & str)
-{
-    nsresult rv;
-    PRBool  exists;
-
-    if (Initialized()) {
-        NS_ASSERTION(PR_FALSE, "Cannot switch cache directory when initialized");
-        return;
-    }
-
-    if (!parentDir) {
-        mCacheDirectory = nsnull;
-        return;
-    }
-
-    // ensure parent directory exists
-    rv = parentDir->Exists(&exists);
-    if (NS_SUCCEEDED(rv) && !exists)
-        rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
-    if (NS_FAILED(rv))  return;
-
-    // ensure cache directory exists
-    nsCOMPtr<nsIFile> directory;
-
-    rv = parentDir->Clone(getter_AddRefs(directory));
-    if (NS_FAILED(rv))  return;
-    rv = directory->AppendNative(str);
-    if (NS_FAILED(rv))  return;
-
-    mCacheDirectory = do_QueryInterface(directory);
-}
-
-
 
 void
 nsDiskCacheDevice::getCacheDirectory(nsILocalFile ** result)
 {
     *result = mCacheDirectory;
     NS_IF_ADDREF(*result);
 }
 
--- a/netwerk/cache/src/nsDiskCacheDevice.h
+++ b/netwerk/cache/src/nsDiskCacheDevice.h
@@ -88,21 +88,16 @@ public:
     virtual nsresult        EvictEntries(const char * clientID);
 
     /**
      * Preference accessors
      */
     void                    SetCacheParentDirectory(nsILocalFile * parentDir);
     void                    SetCapacity(PRUint32  capacity);
 
-    // XXX: This is here to support the offline cache, and can be removed
-    // XXX: once it has its own cache implementation
-    void                    SetCacheParentDirectoryAndName(nsILocalFile * parentDir,
-                                                           const nsACString & str);
-
 /* private: */
 
     void                    getCacheDirectory(nsILocalFile ** result);
     PRUint32                getCacheCapacity();
     PRUint32                getCacheSize();
     PRUint32                getEntryCount();
     
     nsDiskCacheMap *        CacheMap()    { return &mCacheMap; }
--- a/netwerk/cache/src/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/src/nsDiskCacheDeviceSQL.cpp
@@ -15,65 +15,52 @@
  * The Original Code is Mozilla.
  *
  * The Initial Developer of the Original Code is IBM Corporation.
  * Portions created by IBM Corporation are Copyright (C) 2004
  * IBM Corporation. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@meer.net>
+ *   Dave Camp <dcamp@mozilla.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 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 "private/pprio.h"
-
-#include "prlong.h"
-
 #include "nsCache.h"
 #include "nsDiskCache.h"
 #include "nsDiskCacheDeviceSQL.h"
+#include "nsCacheService.h"
 
 #include "nsNetUtil.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsPrintfCString.h"
 #include "nsCRT.h"
 
 #include "mozIStorageService.h"
 #include "mozIStorageStatement.h"
-#include "mozStorageCID.h"
+#include "mozIStorageFunction.h"
+#include "mozStorageHelper.h"
 
 #include "nsICacheVisitor.h"
 #include "nsISeekableStream.h"
 
-static const char DISK_CACHE_DEVICE_ID[] = { "disk" };
+static const char OFFLINE_CACHE_DEVICE_ID[] = { "offline" };
 
 #define LOG(args) CACHE_LOG_DEBUG(args)
 
 
 /*****************************************************************************
  * helpers
  */
 
@@ -115,101 +102,175 @@ class AutoResetStatement
     AutoResetStatement(mozIStorageStatement *s)
       : mStatement(s) {}
     ~AutoResetStatement() { mStatement->Reset(); }
     mozIStorageStatement *operator->() { return mStatement; }
   private:
     mozIStorageStatement *mStatement;
 };
 
-/******************************************************************************
- *  nsDiskCache
- *****************************************************************************/
+class EvictionObserver
+{
+  public:
+    EvictionObserver(mozIStorageConnection *db)
+      : mDB(db)
+    {
+      mDB->ExecuteSimpleSQL(
+          NS_LITERAL_CSTRING("CREATE TEMP TRIGGER cache_on_delete AFTER DELETE"
+                             " ON moz_cache FOR EACH ROW BEGIN SELECT"
+                             " cache_eviction_observer("
+                             "  OLD.clientID, OLD.key, OLD.generation);"
+                             " END;"));
+    }
+
+    ~EvictionObserver()
+    {
+      mDB->ExecuteSimpleSQL(
+        NS_LITERAL_CSTRING("DROP TRIGGER cache_on_delete;"));
+    }
+
+  private:
+    mozIStorageConnection *mDB;
+};
 
 #define DCACHE_HASH_MAX  LL_MAXINT
 #define DCACHE_HASH_BITS 64
 
 /**
- *  nsDiskCache::Hash(const char * key)
+ *  nsOfflineCache::Hash(const char * key)
  *
- *  This algorithm of this method implies nsDiskCacheRecords will be stored
+ *  This algorithm of this method implies nsOfflineCacheRecords will be stored
  *  in a certain order on disk.  If the algorithm changes, existing cache
  *  map files may become invalid, and therefore the kCurrentVersion needs
  *  to be revised.
  */
 static PRUint64
 DCacheHash(const char * key)
 {
   PRUint64 h = 0;
   for (const PRUint8* s = (PRUint8*) key; *s != '\0'; ++s)
     h = (h >> (DCACHE_HASH_BITS - 4)) ^ (h << 4) ^ *s;
   return (h == 0 ? DCACHE_HASH_MAX : h);
 }
 
+/******************************************************************************
+ * nsOfflineCacheEvictionFunction
+ */
 
-nsresult
-nsDiskCache::Truncate(PRFileDesc *  fd, PRUint32  newEOF)
+class nsOfflineCacheEvictionFunction : public mozIStorageFunction {
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_MOZISTORAGEFUNCTION
+
+  nsOfflineCacheEvictionFunction(nsOfflineCacheDevice *device)
+    : mDevice(device)
+  {}
+
+private:
+  nsOfflineCacheDevice *mDevice;
+
+};
+
+NS_IMPL_ISUPPORTS1(nsOfflineCacheEvictionFunction, mozIStorageFunction)
+
+// helper function for directly exposing the same data file binding
+// path algorithm used in nsOfflineCacheBinding::Create
+static nsresult
+GetCacheDataFile(nsIFile *cacheDir, const char *cid, const char *key,
+                 int generation, nsCOMPtr<nsIFile> &file)
 {
-  // use modified SetEOF from nsFileStreams::SetEOF()
+  cacheDir->Clone(getter_AddRefs(file));
+  if (!file)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  nsCAutoString fullKey;
+  fullKey.Append(cid);
+  fullKey.Append(':');
+  fullKey.Append(key);
+
+  PRUint64 hash = DCacheHash(fullKey.get());
+
+  PRUint32 dir1 = (PRUint32) (hash & 0x0F);
+  PRUint32 dir2 = (PRUint32)((hash & 0xF0) >> 4);
+
+  hash >>= 8;
+
+  file->AppendNative(nsPrintfCString("%X", dir1));
+  file->AppendNative(nsPrintfCString("%X", dir2));
+
+  char leaf[64];
+  PR_snprintf(leaf, sizeof(leaf), "%014llX-%X", hash, generation);
+  return file->AppendNative(nsDependentCString(leaf));
+}
 
-#if defined(XP_UNIX) || defined(XP_BEOS)
-  if (ftruncate(PR_FileDesc2NativeHandle(fd), newEOF) != 0) {
-    NS_ERROR("ftruncate failed");
-    return NS_ERROR_FAILURE;
+NS_IMETHODIMP
+nsOfflineCacheEvictionFunction::OnFunctionCall(mozIStorageValueArray *values)
+{
+  LOG(("nsOfflineCacheDevice::EvictionObserver\n"));
+
+  PRUint32 numEntries;
+  nsresult rv = values->GetNumEntries(&numEntries);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ASSERTION(numEntries == 3, "unexpected number of arguments");
+
+  PRUint32 valueLen;
+  const char *cid = values->AsSharedUTF8String(0, &valueLen);
+  const char *key = values->AsSharedUTF8String(1, &valueLen);
+  int generation  = values->AsInt32(2);
+
+  nsCOMPtr<nsIFile> file;
+  rv = GetCacheDataFile(mDevice->CacheDirectory(), cid, key,
+                        generation, file);
+  if (NS_FAILED(rv))
+  {
+    LOG(("GetCacheDataFile [cid=%s key=%s generation=%d] failed [rv=%x]!\n",
+         cid, key, generation, rv));
+    return rv;
   }
 
-#elif defined(XP_WIN)
-  PRInt32 cnt = PR_Seek(fd, newEOF, PR_SEEK_SET);
-  if (cnt == -1)  return NS_ERROR_FAILURE;
-  if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(fd))) {
-    NS_ERROR("SetEndOfFile failed");
-    return NS_ERROR_FAILURE;
-  }
+#if defined(PR_LOGGING)
+  nsCAutoString path;
+  file->GetNativePath(path);
+  LOG(("  removing %s\n", path.get()));
+#endif
 
-#elif defined(XP_OS2)
-  if (DosSetFileSize((HFILE) PR_FileDesc2NativeHandle(fd), newEOF) != NO_ERROR) {
-    NS_ERROR("DosSetFileSize failed");
-    return NS_ERROR_FAILURE;
-  }
-#else
-  // add implementations for other platforms here
-#endif
+  file->Remove(PR_FALSE);
+
   return NS_OK;
 }
 
-
 /******************************************************************************
- * nsDiskCacheDeviceInfo
+ * nsOfflineCacheDeviceInfo
  */
 
-class nsDiskCacheDeviceInfo : public nsICacheDeviceInfo
+class nsOfflineCacheDeviceInfo : public nsICacheDeviceInfo
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICACHEDEVICEINFO
 
-  nsDiskCacheDeviceInfo(nsDiskCacheDevice* device)
+  nsOfflineCacheDeviceInfo(nsOfflineCacheDevice* device)
     : mDevice(device)
   {}
-    
+
 private:
-  nsDiskCacheDevice* mDevice;
+  nsOfflineCacheDevice* mDevice;
 };
 
-NS_IMPL_ISUPPORTS1(nsDiskCacheDeviceInfo, nsICacheDeviceInfo)
+NS_IMPL_ISUPPORTS1(nsOfflineCacheDeviceInfo, nsICacheDeviceInfo)
 
 NS_IMETHODIMP
-nsDiskCacheDeviceInfo::GetDescription(char **aDescription)
+nsOfflineCacheDeviceInfo::GetDescription(char **aDescription)
 {
-  *aDescription = nsCRT::strdup("Disk cache device");
+  *aDescription = NS_strdup("Offline cache device");
   return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 NS_IMETHODIMP
-nsDiskCacheDeviceInfo::GetUsageReport(char ** usageReport)
+nsOfflineCacheDeviceInfo::GetUsageReport(char ** usageReport)
 {
   nsCAutoString buffer;
   buffer.AppendLiteral("\n<tr>\n<td><b>Cache Directory:</b></td>\n<td><tt> ");
   
   nsILocalFile *cacheDir = mDevice->CacheDirectory();
   if (!cacheDir)
     return NS_OK;
 
@@ -224,57 +285,58 @@ nsDiskCacheDeviceInfo::GetUsageReport(ch
   *usageReport = ToNewCString(buffer);
   if (!*usageReport)
     return NS_ERROR_OUT_OF_MEMORY;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDiskCacheDeviceInfo::GetEntryCount(PRUint32 *aEntryCount)
+nsOfflineCacheDeviceInfo::GetEntryCount(PRUint32 *aEntryCount)
 {
   *aEntryCount = mDevice->EntryCount();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDiskCacheDeviceInfo::GetTotalSize(PRUint32 *aTotalSize)
+nsOfflineCacheDeviceInfo::GetTotalSize(PRUint32 *aTotalSize)
 {
   *aTotalSize = mDevice->CacheSize();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDiskCacheDeviceInfo::GetMaximumSize(PRUint32 *aMaximumSize)
+nsOfflineCacheDeviceInfo::GetMaximumSize(PRUint32 *aMaximumSize)
 {
   *aMaximumSize = mDevice->CacheCapacity();
   return NS_OK;
 }
 
-
 /******************************************************************************
- * nsDiskCacheBinding
+ * nsOfflineCacheBinding
  */
 
-class nsDiskCacheBinding : public nsISupports
+class nsOfflineCacheBinding : public nsISupports
 {
 public:
   NS_DECL_ISUPPORTS
 
-  static nsDiskCacheBinding *
+  static nsOfflineCacheBinding *
       Create(nsIFile *cacheDir, const char *key, int generation);
 
   nsCOMPtr<nsIFile> mDataFile;
   int               mGeneration;
 };
 
-NS_IMPL_THREADSAFE_ISUPPORTS0(nsDiskCacheBinding)
+NS_IMPL_THREADSAFE_ISUPPORTS0(nsOfflineCacheBinding)
 
-nsDiskCacheBinding *
-nsDiskCacheBinding::Create(nsIFile *cacheDir, const char *key, int generation)
+nsOfflineCacheBinding *
+nsOfflineCacheBinding::Create(nsIFile *cacheDir,
+                              const char *key,
+                              int generation)
 {
   nsCOMPtr<nsIFile> file;
   cacheDir->Clone(getter_AddRefs(file));
   if (!file)
     return nsnull;
 
   PRUint64 hash = DCacheHash(key);
 
@@ -315,312 +377,282 @@ nsDiskCacheBinding::Create(nsIFile *cach
   else
   {
     PR_snprintf(leaf, sizeof(leaf), "%014llX-%X", hash, generation);
     rv = file->AppendNative(nsDependentCString(leaf));
     if (NS_FAILED(rv))
       return nsnull;
   }
 
-  nsDiskCacheBinding *binding = new nsDiskCacheBinding;
+  nsOfflineCacheBinding *binding = new nsOfflineCacheBinding;
   if (!binding)
     return nsnull;
 
   binding->mDataFile.swap(file);
   binding->mGeneration = generation;
   return binding;
 }
 
-// helper function for directly exposing the same data file binding
-// path algorithm used in nsDiskCacheBinding::Create
-static nsresult
-GetCacheDataFile(nsIFile *cacheDir, const char *cid, const char *key,
-                 int generation, nsCOMPtr<nsIFile> &file)
-{
-  cacheDir->Clone(getter_AddRefs(file));
-  if (!file)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  nsCAutoString fullKey;
-  fullKey.Append(cid);
-  fullKey.Append(':');
-  fullKey.Append(key);
-
-  PRUint64 hash = DCacheHash(fullKey.get());
-
-  PRUint32 dir1 = (PRUint32) (hash & 0x0F);
-  PRUint32 dir2 = (PRUint32)((hash & 0xF0) >> 4);
-
-  hash >>= 8;
-
-  file->AppendNative(nsPrintfCString("%X", dir1));
-  file->AppendNative(nsPrintfCString("%X", dir2));
-
-  char leaf[64];
-  PR_snprintf(leaf, sizeof(leaf), "%014llX-%X", hash, generation);
-  return file->AppendNative(nsDependentCString(leaf));
-}
-
-
 /******************************************************************************
- * nsDiskCacheRecord
+ * nsOfflineCacheRecord
  */
 
-struct nsDiskCacheRecord
+struct nsOfflineCacheRecord
 {
   const char    *clientID;
   const char    *key;
   const PRUint8 *metaData;
   PRUint32       metaDataLen;
   PRInt32        generation;
   PRInt32        flags;
   PRInt32        dataSize;
   PRInt32        fetchCount;
   PRInt64        lastFetched;
   PRInt64        lastModified;
   PRInt64        expirationTime;
 };
 
 static nsCacheEntry *
-CreateCacheEntry(nsDiskCacheDevice *device,
+CreateCacheEntry(nsOfflineCacheDevice *device,
                  const nsCString *fullKey,
-                 const nsDiskCacheRecord &rec)
+                 const nsOfflineCacheRecord &rec)
 {
   if (rec.flags != 0)
   {
     LOG(("refusing to load busy entry\n"));
     return nsnull;
   }
 
   nsCacheEntry *entry;
   
   nsresult rv = nsCacheEntry::Create(fullKey->get(), // XXX enable sharing
                                      nsICache::STREAM_BASED,
-                                     nsICache::STORE_ON_DISK,
+                                     nsICache::STORE_OFFLINE,
                                      device, &entry);
   if (NS_FAILED(rv))
     return nsnull;
 
   entry->SetFetchCount((PRUint32) rec.fetchCount);
   entry->SetLastFetched(SecondsFromPRTime(rec.lastFetched));
   entry->SetLastModified(SecondsFromPRTime(rec.lastModified));
   entry->SetExpirationTime(SecondsFromPRTime(rec.expirationTime));
   entry->SetDataSize((PRUint32) rec.dataSize);
 
   entry->UnflattenMetaData((const char *) rec.metaData, rec.metaDataLen);
 
   // create a binding object for this entry
-  nsDiskCacheBinding *binding =
-      nsDiskCacheBinding::Create(device->CacheDirectory(),
-                                 fullKey->get(),
-                                 rec.generation);
+  nsOfflineCacheBinding *binding =
+      nsOfflineCacheBinding::Create(device->CacheDirectory(),
+                                    fullKey->get(),
+                                    rec.generation);
   if (!binding)
   {
     delete entry;
     return nsnull;
   }
   entry->SetData(binding);
 
   return entry;
 }
 
 
 /******************************************************************************
- * nsDiskCacheEntryInfo
+ * nsOfflineCacheEntryInfo
  */
 
-class nsDiskCacheEntryInfo : public nsICacheEntryInfo
+class nsOfflineCacheEntryInfo : public nsICacheEntryInfo
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICACHEENTRYINFO
 
-  nsDiskCacheRecord *mRec;
+  nsOfflineCacheRecord *mRec;
 };
 
-NS_IMPL_ISUPPORTS1(nsDiskCacheEntryInfo, nsICacheEntryInfo)
+NS_IMPL_ISUPPORTS1(nsOfflineCacheEntryInfo, nsICacheEntryInfo)
 
 NS_IMETHODIMP
-nsDiskCacheEntryInfo::GetClientID(char **result)
+nsOfflineCacheEntryInfo::GetClientID(char **result)
 {
-  *result = nsCRT::strdup(mRec->clientID);
+  *result = NS_strdup(mRec->clientID);
   return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 NS_IMETHODIMP
-nsDiskCacheEntryInfo::GetDeviceID(char ** deviceID)
+nsOfflineCacheEntryInfo::GetDeviceID(char ** deviceID)
 {
-  *deviceID = nsCRT::strdup(DISK_CACHE_DEVICE_ID);
+  *deviceID = NS_strdup(OFFLINE_CACHE_DEVICE_ID);
   return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 NS_IMETHODIMP
-nsDiskCacheEntryInfo::GetKey(nsACString &clientKey)
+nsOfflineCacheEntryInfo::GetKey(nsACString &clientKey)
 {
   clientKey.Assign(mRec->key);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDiskCacheEntryInfo::GetFetchCount(PRInt32 *aFetchCount)
+nsOfflineCacheEntryInfo::GetFetchCount(PRInt32 *aFetchCount)
 {
   *aFetchCount = mRec->fetchCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDiskCacheEntryInfo::GetLastFetched(PRUint32 *aLastFetched)
+nsOfflineCacheEntryInfo::GetLastFetched(PRUint32 *aLastFetched)
 {
   *aLastFetched = SecondsFromPRTime(mRec->lastFetched);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDiskCacheEntryInfo::GetLastModified(PRUint32 *aLastModified)
+nsOfflineCacheEntryInfo::GetLastModified(PRUint32 *aLastModified)
 {
   *aLastModified = SecondsFromPRTime(mRec->lastModified);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDiskCacheEntryInfo::GetExpirationTime(PRUint32 *aExpirationTime)
+nsOfflineCacheEntryInfo::GetExpirationTime(PRUint32 *aExpirationTime)
 {
   *aExpirationTime = SecondsFromPRTime(mRec->expirationTime);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDiskCacheEntryInfo::IsStreamBased(PRBool *aStreamBased)
+nsOfflineCacheEntryInfo::IsStreamBased(PRBool *aStreamBased)
 {
   *aStreamBased = PR_TRUE;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsDiskCacheEntryInfo::GetDataSize(PRUint32 *aDataSize)
+NS_IMETHODIMP
+nsOfflineCacheEntryInfo::GetDataSize(PRUint32 *aDataSize)
 {
   *aDataSize = mRec->dataSize;
   return NS_OK;
 }
 
 
 /******************************************************************************
- * nsDiskCacheDevice
+ * nsOfflineCacheDevice
  */
 
-nsDiskCacheDevice::nsDiskCacheDevice()
+nsOfflineCacheDevice::nsOfflineCacheDevice()
   : mDB(nsnull)
   , mCacheCapacity(0)
   , mDeltaCounter(0)
 {
 }
 
-nsDiskCacheDevice::~nsDiskCacheDevice()
+nsOfflineCacheDevice::~nsOfflineCacheDevice()
 {
   Shutdown();
 }
 
 PRUint32
-nsDiskCacheDevice::CacheSize()
+nsOfflineCacheDevice::CacheSize()
 {
   AutoResetStatement statement(mStatement_CacheSize);
 
   PRBool hasRows;
   nsresult rv = statement->ExecuteStep(&hasRows);
   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasRows, 0);
   
   return (PRUint32) statement->AsInt32(0);
 }
 
 PRUint32
-nsDiskCacheDevice::EntryCount()
+nsOfflineCacheDevice::EntryCount()
 {
   AutoResetStatement statement(mStatement_EntryCount);
 
   PRBool hasRows;
   nsresult rv = statement->ExecuteStep(&hasRows);
   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasRows, 0);
 
   return (PRUint32) statement->AsInt32(0);
 }
 
 nsresult
-nsDiskCacheDevice::UpdateEntry(nsCacheEntry *entry)
+nsOfflineCacheDevice::UpdateEntry(nsCacheEntry *entry)
 {
   // Decompose the key into "ClientID" and "Key"
   nsCAutoString keyBuf;
   const char *cid, *key;
   if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
     return NS_ERROR_UNEXPECTED;
 
   nsCString metaDataBuf;
   PRUint32 mdSize = entry->MetaDataSize();
   if (!EnsureStringLength(metaDataBuf, mdSize))
     return NS_ERROR_OUT_OF_MEMORY;
   char *md = metaDataBuf.BeginWriting();
   entry->FlattenMetaData(md, mdSize);
 
-  nsDiskCacheRecord rec;
+  nsOfflineCacheRecord rec;
   rec.metaData = (const PRUint8 *) md;
   rec.metaDataLen = mdSize;
   rec.flags = 0;  // mark entry as inactive
   rec.dataSize = entry->DataSize();
   rec.fetchCount = entry->FetchCount();
   rec.lastFetched = PRTimeFromSeconds(entry->LastFetched());
   rec.lastModified = PRTimeFromSeconds(entry->LastModified());
   rec.expirationTime = PRTimeFromSeconds(entry->ExpirationTime());
 
   AutoResetStatement statement(mStatement_UpdateEntry);
 
   nsresult rv;
-  rv  = statement->BindDataParameter(0, rec.metaData, rec.metaDataLen);
+  rv  = statement->BindBlobParameter(0, rec.metaData, rec.metaDataLen);
   rv |= statement->BindInt32Parameter(1, rec.flags);
   rv |= statement->BindInt32Parameter(2, rec.dataSize);
   rv |= statement->BindInt32Parameter(3, rec.fetchCount);
   rv |= statement->BindInt64Parameter(4, rec.lastFetched);
   rv |= statement->BindInt64Parameter(5, rec.lastModified);
   rv |= statement->BindInt64Parameter(6, rec.expirationTime);
-  rv |= statement->BindCStringParameter(7, cid);
-  rv |= statement->BindCStringParameter(8, key);
+  rv |= statement->BindUTF8StringParameter(7, nsDependentCString(cid));
+  rv |= statement->BindUTF8StringParameter(8, nsDependentCString(key));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRBool hasRows;
   rv = statement->ExecuteStep(&hasRows);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ASSERTION(!hasRows, "UPDATE should not result in output");
   return rv;
 }
 
 nsresult
-nsDiskCacheDevice::UpdateEntrySize(nsCacheEntry *entry, PRUint32 newSize)
+nsOfflineCacheDevice::UpdateEntrySize(nsCacheEntry *entry, PRUint32 newSize)
 {
   // Decompose the key into "ClientID" and "Key"
   nsCAutoString keyBuf;
   const char *cid, *key;
   if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
     return NS_ERROR_UNEXPECTED;
 
   AutoResetStatement statement(mStatement_UpdateEntrySize);
 
   nsresult rv;
   rv  = statement->BindInt32Parameter(0, newSize);
-  rv |= statement->BindCStringParameter(1, cid);
-  rv |= statement->BindCStringParameter(2, key);
+  rv |= statement->BindUTF8StringParameter(1, nsDependentCString(cid));
+  rv |= statement->BindUTF8StringParameter(2, nsDependentCString(key));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRBool hasRows;
   rv = statement->ExecuteStep(&hasRows);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ASSERTION(!hasRows, "UPDATE should not result in output");
   return rv;
 }
 
 nsresult
-nsDiskCacheDevice::DeleteEntry(nsCacheEntry *entry, PRBool deleteData)
+nsOfflineCacheDevice::DeleteEntry(nsCacheEntry *entry, PRBool deleteData)
 {
   if (deleteData)
   {
     nsresult rv = DeleteData(entry);
     if (NS_FAILED(rv))
       return rv;
   }
 
@@ -628,128 +660,62 @@ nsDiskCacheDevice::DeleteEntry(nsCacheEn
   nsCAutoString keyBuf;
   const char *cid, *key;
   if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
     return NS_ERROR_UNEXPECTED;
 
   AutoResetStatement statement(mStatement_DeleteEntry);
 
   nsresult rv;
-  rv  = statement->BindCStringParameter(0, cid);
-  rv |= statement->BindCStringParameter(1, key);
+  rv  = statement->BindUTF8StringParameter(0, nsDependentCString(cid));
+  rv |= statement->BindUTF8StringParameter(1, nsDependentCString(key));
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRBool hasRows;
   rv = statement->ExecuteStep(&hasRows);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ASSERTION(!hasRows, "DELETE should not result in output");
   return rv;
 }
 
 nsresult
-nsDiskCacheDevice::DeleteData(nsCacheEntry *entry)
+nsOfflineCacheDevice::DeleteData(nsCacheEntry *entry)
 {
-  nsDiskCacheBinding *binding = (nsDiskCacheBinding *) entry->Data();
+  nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
   NS_ENSURE_STATE(binding);
 
   return binding->mDataFile->Remove(PR_FALSE);
 }
 
-nsresult
-nsDiskCacheDevice::EnableEvictionObserver()
-{
-#if 0
-  // use CreateTrigger .. maybe do this only once, and have a member
-  // variable to control whether or not it is active.
-
-  int res =
-      sqlite3_exec(mDB, "CREATE TEMP TRIGGER cache_on_delete AFTER DELETE"
-                        " ON moz_cache FOR EACH ROW BEGIN SELECT"
-                        " cache_eviction_observer("
-                        "  OLD.clientID, OLD.key, OLD.generation);"
-                        " END;", NULL, NULL, NULL);
-  NS_ENSURE_SQLITE_RESULT(res, NS_ERROR_UNEXPECTED);
-#endif
-
-  return NS_OK;
-}
-
-nsresult
-nsDiskCacheDevice::DisableEvictionObserver()
-{
-#if 0
-  int res = sqlite3_exec(mDB, "DROP TRIGGER cache_on_delete;",
-                         NULL, NULL, NULL);
-  NS_ENSURE_SQLITE_RESULT(res, NS_ERROR_UNEXPECTED);
-#endif
-
-  return NS_OK;
-}
-
-#if 0
-/* static */ void
-nsDiskCacheDevice::EvictionObserver(sqlite3_context *ctx, int narg,
-                                    sqlite3_value **values)
-{
-  LOG(("nsDiskCacheDevice::EvictionObserver\n"));
-
-  nsDiskCacheDevice *device = (nsDiskCacheDevice *) sqlite3_user_data(ctx);
-
-  NS_ASSERTION(narg == 3, "unexpected number of arguments");
-  const char *cid = (const char *) sqlite3_value_text(values[0]);
-  const char *key = (const char *) sqlite3_value_text(values[1]);
-  int generation  =                sqlite3_value_int(values[2]);
-
-  nsCOMPtr<nsIFile> file;
-  nsresult rv = GetCacheDataFile(device->CacheDirectory(), cid, key,
-                                 generation, file);
-  if (NS_FAILED(rv))
-  {
-    LOG(("GetCacheDataFile [cid=%s key=%s generation=%d] failed [rv=%x]!\n",
-        cid, key, generation, rv));
-    return;
-  }
-
-#if defined(PR_LOGGING)
-  nsCAutoString path;
-  file->GetNativePath(path);
-  LOG(("  removing %s\n", path.get()));
-#endif
-
-  file->Remove(PR_FALSE);
-}
-#endif
-
-
 /**
  * nsCacheDevice implementation
  */
 
 nsresult
-nsDiskCacheDevice::Init()
+nsOfflineCacheDevice::Init()
 {
   NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
 
   // SetCacheParentDirectory must have been called
   NS_ENSURE_TRUE(mCacheDirectory, NS_ERROR_UNEXPECTED);
 
   // make sure the cache directory exists
   nsresult rv = EnsureDir(mCacheDirectory);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // build path to index file
   nsCOMPtr<nsIFile> indexFile; 
   rv = mCacheDirectory->Clone(getter_AddRefs(indexFile));
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = indexFile->AppendNative(NS_LITERAL_CSTRING("index.db"));
+  rv = indexFile->AppendNative(NS_LITERAL_CSTRING("index.sqlite"));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<mozIStorageService> ss =
-      do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
+      do_GetService("@mozilla.org/storage/service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = ss->OpenDatabase(indexFile, getter_AddRefs(mDB));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF;"));
 
   // XXX ... other initialization steps
@@ -772,111 +738,119 @@ nsDiskCacheDevice::Init()
                          "  DataSize        INTEGER,\n"
                          "  FetchCount      INTEGER,\n"
                          "  LastFetched     INTEGER,\n"
                          "  LastModified    INTEGER,\n"
                          "  ExpirationTime  INTEGER\n"
                          ");\n"));
   // maybe the table already exists, so don't bother checking for errors.
 
+  // build the ownership table
+  mDB->ExecuteSimpleSQL(
+      NS_LITERAL_CSTRING("CREATE TABLE moz_cache_owners (\n"
+                         " ClientID TEXT,\n"
+                         " Domain TEXT,\n"
+                         " URI TEXT,\n"
+                         " Key TEXT\n"
+                         ");\n"));
+  // maybe the table already exists, so don't bother checking for errors.
+
   mDB->ExecuteSimpleSQL(
       NS_LITERAL_CSTRING("CREATE UNIQUE INDEX moz_cache_index"
                          " ON moz_cache (ClientID, Key);"));
   // maybe the index already exists, so don't bother checking for errors.
 
-#if 0
-  // create cache_eviction_observer
-  res = sqlite3_create_function(mDB, "cache_eviction_observer",
-                                3, SQLITE_UTF8, this,
-                                nsDiskCacheDevice::EvictionObserver,
-                                NULL, NULL);
-  if (res != SQLITE_OK)
-    LOG(("sqlite3_create_function failed [res=%d]\n", res));
-#endif
+  nsCOMPtr<mozIStorageFunction> evictionFunction =
+    new nsOfflineCacheEvictionFunction(this);
+  if (!evictionFunction) return NS_ERROR_OUT_OF_MEMORY;
+
+  rv = mDB->CreateFunction("cache_eviction_observer", 3, evictionFunction);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // create all (most) of our statements up front
   struct {
     nsCOMPtr<mozIStorageStatement> &statement;
     const char *sql;
   } prepared[] = {
-    { mStatement_CacheSize,       "SELECT Sum(DataSize) from moz_cache;" },
-    { mStatement_EntryCount,      "SELECT count(*) from moz_cache;" },
-    { mStatement_UpdateEntry,     "UPDATE moz_cache SET MetaData = ?, Flags = ?, DataSize = ?, FetchCount = ?, LastFetched = ?, LastModified = ?, ExpirationTime = ? WHERE ClientID = ? AND Key = ?;" },
-    { mStatement_UpdateEntrySize, "UPDATE moz_cache SET DataSize = ? WHERE ClientID = ? AND Key = ?;" },
-    { mStatement_DeleteEntry,     "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ?;" },
-    { mStatement_FindEntry,       "SELECT MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime FROM moz_cache WHERE ClientID = ? AND Key = ?;" },
-    { mStatement_BindEntry,       "INSERT INTO moz_cache VALUES(?,?,?,?,?,?,?,?,?,?);" }
+    { mStatement_CacheSize,         "SELECT Sum(DataSize) from moz_cache;" },
+    { mStatement_EntryCount,        "SELECT count(*) from moz_cache;" },
+    { mStatement_UpdateEntry,       "UPDATE moz_cache SET MetaData = ?, Flags = ?, DataSize = ?, FetchCount = ?, LastFetched = ?, LastModified = ?, ExpirationTime = ? WHERE ClientID = ? AND Key = ?;" },
+    { mStatement_UpdateEntrySize,   "UPDATE moz_cache SET DataSize = ? WHERE ClientID = ? AND Key = ?;" },
+    { mStatement_UpdateEntryFlags,  "UPDATE moz_cache SET Flags = ? WHERE ClientID = ? AND Key = ?;" },
+    { mStatement_DeleteEntry,       "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ?;" },
+    { mStatement_FindEntry,         "SELECT MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime FROM moz_cache WHERE ClientID = ? AND Key = ?;" },
+    { mStatement_BindEntry,         "INSERT INTO moz_cache (ClientID, Key, MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime) VALUES(?,?,?,?,?,?,?,?,?,?);" },
+    { mStatement_ClearOwnership,    "DELETE FROM moz_cache_owners WHERE ClientId = ? AND Domain = ? AND URI = ?;" },
+    { mStatement_RemoveOwnership,   "DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?;" },
+    { mStatement_ClearDomain,       "DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?;" },
+    { mStatement_AddOwnership,      "INSERT INTO moz_cache_owners (ClientID, Domain, URI, Key) VALUES (?, ?, ?, ?);" },
+    { mStatement_CheckOwnership,    "SELECT Key From moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?;" },
+    { mStatement_ListOwned,         "SELECT Key FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ?;" },
+    { mStatement_DeleteUnowned,     "DELETE FROM moz_cache WHERE rowid IN (SELECT moz_cache.rowid FROM moz_cache LEFT OUTER JOIN moz_cache_owners ON (moz_cache.ClientID = moz_cache_owners.ClientID AND moz_cache.Key = moz_cache_owners.Key) WHERE moz_cache.ClientID = ? AND moz_cache_owners.Domain ISNULL);" },
   };
   for (PRUint32 i=0; i<NS_ARRAY_LENGTH(prepared); ++i)
   {
     rv |= mDB->CreateStatement(nsDependentCString(prepared[i].sql),
                                getter_AddRefs(prepared[i].statement));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Clear up any dangling active flags
+  rv = mDB->ExecuteSimpleSQL(
+         NS_LITERAL_CSTRING("UPDATE moz_cache"
+                            " SET Flags=(Flags & ~1)"
+                            " WHERE (Flags & 1);"));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return NS_OK;
 }
 
 nsresult
-nsDiskCacheDevice::Shutdown()
+nsOfflineCacheDevice::Shutdown()
 {
   NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED);
 
-#if 0
-  int res;
-
-  // delete cache_eviction_observer
-  res = sqlite3_create_function(mDB, "cache_eviction_observer", 3, SQLITE_UTF8,
-                                NULL, NULL, NULL, NULL);
-  if (res != SQLITE_OK)
-    LOG(("sqlite3_create_function failed [res=%d]\n", res));
-  
-  res = sqlite3_close(mDB);
-  NS_ENSURE_SQLITE_RESULT(res, NS_ERROR_UNEXPECTED);
-
-  mDB = nsnull;
-#endif
-
   mDB = 0;
   return NS_OK;
 }
 
 const char *
-nsDiskCacheDevice::GetDeviceID()
+nsOfflineCacheDevice::GetDeviceID()
 {
-  return DISK_CACHE_DEVICE_ID;
+  return OFFLINE_CACHE_DEVICE_ID;
 }
 
 nsCacheEntry *
-nsDiskCacheDevice::FindEntry(nsCString *fullKey, PRBool *collision)
+nsOfflineCacheDevice::FindEntry(nsCString *fullKey, PRBool *collision)
 {
-  LOG(("nsDiskCacheDevice::FindEntry [key=%s]\n", fullKey->get()));
+  LOG(("nsOfflineCacheDevice::FindEntry [key=%s]\n", fullKey->get()));
 
   // SELECT * FROM moz_cache WHERE key = ?
 
   // Decompose the key into "ClientID" and "Key"
   nsCAutoString keyBuf;
   const char *cid, *key;
   if (!DecomposeCacheEntryKey(fullKey, &cid, &key, keyBuf))
     return nsnull;
 
   AutoResetStatement statement(mStatement_FindEntry);
 
   nsresult rv;
-  rv  = statement->BindCStringParameter(0, cid);
-  rv |= statement->BindCStringParameter(1, key);
+  rv  = statement->BindUTF8StringParameter(0, nsDependentCString(cid));
+  rv |= statement->BindUTF8StringParameter(1, nsDependentCString(key));
   NS_ENSURE_SUCCESS(rv, nsnull);
 
   PRBool hasRows;
   rv = statement->ExecuteStep(&hasRows);
   if (NS_FAILED(rv) || !hasRows)
     return nsnull; // entry not found
 
-  nsDiskCacheRecord rec;
-  statement->AsSharedBlob(0, (const void **) &rec.metaData, &rec.metaDataLen);
+  nsOfflineCacheRecord rec;
+  statement->GetSharedBlob(0, &rec.metaDataLen,
+                           (const PRUint8 **) &rec.metaData);
   rec.generation     = statement->AsInt32(1);
   rec.flags          = statement->AsInt32(2);
   rec.dataSize       = statement->AsInt32(3);
   rec.fetchCount     = statement->AsInt32(4);
   rec.lastFetched    = statement->AsInt64(5);
   rec.lastModified   = statement->AsInt64(6);
   rec.expirationTime = statement->AsInt64(7);
 
@@ -885,26 +859,63 @@ nsDiskCacheDevice::FindEntry(nsCString *
         rec.generation,
         rec.flags,
         rec.dataSize,
         rec.fetchCount,
         rec.lastFetched,
         rec.lastModified,
         rec.expirationTime));
 
-  return CreateCacheEntry(this, fullKey, rec);
+  nsCacheEntry *entry = CreateCacheEntry(this, fullKey, rec);
+
+  if (entry)
+  {
+    // make sure that the data file exists
+    nsOfflineCacheBinding *binding = (nsOfflineCacheBinding*)entry->Data();
+    PRBool isFile;
+    rv = binding->mDataFile->IsFile(&isFile);
+    if (NS_FAILED(rv) || !isFile)
+    {
+      DeleteEntry(entry, PR_FALSE);
+      delete entry;
+      return nsnull;
+    }
+
+    statement->Reset();
 
-  // XXX we should verify that the corresponding data file exists,
-  //     and if not, then we should delete this row and return null.
+    // mark as active
+    AutoResetStatement updateStatement(mStatement_UpdateEntryFlags);
+    rec.flags |= 0x1;
+    rv |= updateStatement->BindInt32Parameter(0, rec.flags);
+    rv |= updateStatement->BindUTF8StringParameter(1, nsDependentCString(cid));
+    rv |= updateStatement->BindUTF8StringParameter(2, nsDependentCString(key));
+    if (NS_FAILED(rv))
+    {
+      delete entry;
+      return nsnull;
+    }
+
+    rv = updateStatement->ExecuteStep(&hasRows);
+    if (NS_FAILED(rv))
+    {
+      delete entry;
+      return nsnull;
+    }
+
+    NS_ASSERTION(!hasRows, "UPDATE should not result in output");
+  }
+
+  return entry;
 }
 
 nsresult
-nsDiskCacheDevice::DeactivateEntry(nsCacheEntry *entry)
+nsOfflineCacheDevice::DeactivateEntry(nsCacheEntry *entry)
 {
-  LOG(("nsDiskCacheDevice::DeactivateEntry [key=%s]\n", entry->Key()->get()));
+  LOG(("nsOfflineCacheDevice::DeactivateEntry [key=%s]\n",
+       entry->Key()->get()));
 
   // This method is called to inform us that the nsCacheEntry object is going
   // away.  We should persist anything that needs to be persisted, or if the
   // entry is doomed, we can go ahead and clear its storage.
 
   if (entry->IsDoomed())
   {
     // remove corresponding row and file if they exist
@@ -920,23 +931,24 @@ nsDiskCacheDevice::DeactivateEntry(nsCac
 
     // XXX Assumption: the row already exists because it was either created
     // with a call to BindEntry or it was there when we called FindEntry.
 
     UpdateEntry(entry);
   }
 
   delete entry;
+
   return NS_OK;
 }
 
 nsresult
-nsDiskCacheDevice::BindEntry(nsCacheEntry *entry)
+nsOfflineCacheDevice::BindEntry(nsCacheEntry *entry)
 {
-  LOG(("nsDiskCacheDevice::BindEntry [key=%s]\n", entry->Key()->get()));
+  LOG(("nsOfflineCacheDevice::BindEntry [key=%s]\n", entry->Key()->get()));
 
   NS_ENSURE_STATE(!entry->Data());
 
   // This method is called to inform us that we have a new entry.  The entry
   // may collide with an existing entry in our DB, but if that happens we can
   // assume that the entry is not being used.
 
   // INSERT the database row
@@ -947,40 +959,40 @@ nsDiskCacheDevice::BindEntry(nsCacheEntr
 
   // Decompose the key into "ClientID" and "Key"
   nsCAutoString keyBuf;
   const char *cid, *key;
   if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
     return NS_ERROR_UNEXPECTED;
 
   // create binding, pick best generation number
-  nsRefPtr<nsDiskCacheBinding> binding =
-      nsDiskCacheBinding::Create(mCacheDirectory, entry->Key()->get(), -1);
+  nsRefPtr<nsOfflineCacheBinding> binding =
+      nsOfflineCacheBinding::Create(mCacheDirectory, entry->Key()->get(), -1);
   if (!binding)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  nsDiskCacheRecord rec;
+  nsOfflineCacheRecord rec;
   rec.clientID = cid;
   rec.key = key;
   rec.metaData = NULL; // don't write any metadata now.
   rec.metaDataLen = 0;
   rec.generation = binding->mGeneration;
   rec.flags = 0x1;  // mark entry as active, we'll reset this in DeactivateEntry
   rec.dataSize = 0;
   rec.fetchCount = entry->FetchCount();
   rec.lastFetched = PRTimeFromSeconds(entry->LastFetched());
   rec.lastModified = PRTimeFromSeconds(entry->LastModified());
   rec.expirationTime = PRTimeFromSeconds(entry->ExpirationTime());
 
   AutoResetStatement statement(mStatement_BindEntry);
 
   nsresult rv;
-  rv  = statement->BindCStringParameter(0, rec.clientID);
-  rv |= statement->BindCStringParameter(1, rec.key);
-  rv |= statement->BindDataParameter(2, rec.metaData, rec.metaDataLen);
+  rv  = statement->BindUTF8StringParameter(0, nsDependentCString(rec.clientID));
+  rv |= statement->BindUTF8StringParameter(1, nsDependentCString(rec.key));
+  rv |= statement->BindBlobParameter(2, rec.metaData, rec.metaDataLen);
   rv |= statement->BindInt32Parameter(3, rec.generation);
   rv |= statement->BindInt32Parameter(4, rec.flags);
   rv |= statement->BindInt32Parameter(5, rec.dataSize);
   rv |= statement->BindInt32Parameter(6, rec.fetchCount);
   rv |= statement->BindInt64Parameter(7, rec.lastFetched);
   rv |= statement->BindInt64Parameter(8, rec.lastModified);
   rv |= statement->BindInt64Parameter(9, rec.expirationTime);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -991,45 +1003,46 @@ nsDiskCacheDevice::BindEntry(nsCacheEntr
 
   NS_ASSERTION(!hasRows, "INSERT should not result in output");
 
   entry->SetData(binding);
   return NS_OK;
 }
 
 void
-nsDiskCacheDevice::DoomEntry(nsCacheEntry *entry)
+nsOfflineCacheDevice::DoomEntry(nsCacheEntry *entry)
 {
-  LOG(("nsDiskCacheDevice::DoomEntry [key=%s]\n", entry->Key()->get()));
+  LOG(("nsOfflineCacheDevice::DoomEntry [key=%s]\n", entry->Key()->get()));
 
   // This method is called to inform us that we should mark the entry to be
   // deleted when it is no longer in use.
 
   // We can go ahead and delete the corresponding row in our table,
   // but we must not delete the file on disk until we are deactivated.
   
   DeleteEntry(entry, PR_FALSE);
 }
 
 nsresult
-nsDiskCacheDevice::OpenInputStreamForEntry(nsCacheEntry      *entry,
-                                           nsCacheAccessMode  mode,
-                                           PRUint32           offset,
-                                           nsIInputStream   **result)
+nsOfflineCacheDevice::OpenInputStreamForEntry(nsCacheEntry      *entry,
+                                              nsCacheAccessMode  mode,
+                                              PRUint32           offset,
+                                              nsIInputStream   **result)
 {
-  LOG(("nsDiskCacheDevice::OpenInputStreamForEntry [key=%s]\n", entry->Key()->get()));
+  LOG(("nsOfflineCacheDevice::OpenInputStreamForEntry [key=%s]\n",
+       entry->Key()->get()));
 
   *result = nsnull;
 
   NS_ENSURE_TRUE(offset < entry->DataSize(), NS_ERROR_INVALID_ARG);
 
   // return an input stream to the entry's data file.  the stream
   // may be read on a background thread.
 
-  nsDiskCacheBinding *binding = (nsDiskCacheBinding *) entry->Data();
+  nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
   NS_ENSURE_STATE(binding);
 
   nsCOMPtr<nsIInputStream> in;
   NS_NewLocalFileInputStream(getter_AddRefs(in), binding->mDataFile, PR_RDONLY);
   if (!in)
     return NS_ERROR_UNEXPECTED;
 
   // respect |offset| param
@@ -1041,31 +1054,32 @@ nsDiskCacheDevice::OpenInputStreamForEnt
     seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
   }
 
   in.swap(*result);
   return NS_OK;
 }
 
 nsresult
-nsDiskCacheDevice::OpenOutputStreamForEntry(nsCacheEntry       *entry,
-                                            nsCacheAccessMode   mode,
-                                            PRUint32            offset,
-                                            nsIOutputStream   **result)
+nsOfflineCacheDevice::OpenOutputStreamForEntry(nsCacheEntry       *entry,
+                                               nsCacheAccessMode   mode,
+                                               PRUint32            offset,
+                                               nsIOutputStream   **result)
 {
-  LOG(("nsDiskCacheDevice::OpenOutputStreamForEntry [key=%s]\n", entry->Key()->get()));
+  LOG(("nsOfflineCacheDevice::OpenOutputStreamForEntry [key=%s]\n",
+       entry->Key()->get()));
 
   *result = nsnull;
 
   NS_ENSURE_TRUE(offset <= entry->DataSize(), NS_ERROR_INVALID_ARG);
 
   // return an output stream to the entry's data file.  we can assume
   // that the output stream will only be used on the main thread.
 
-  nsDiskCacheBinding *binding = (nsDiskCacheBinding *) entry->Data();
+  nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
   NS_ENSURE_STATE(binding);
 
   nsCOMPtr<nsIOutputStream> out;
   NS_NewLocalFileOutputStream(getter_AddRefs(out), binding->mDataFile,
                               PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
                               00600);
   if (!out)
     return NS_ERROR_UNEXPECTED;
@@ -1084,86 +1098,85 @@ nsDiskCacheDevice::OpenOutputStreamForEn
   if (!bufferedOut)
     return NS_ERROR_UNEXPECTED;
 
   bufferedOut.swap(*result);
   return NS_OK;
 }
 
 nsresult
-nsDiskCacheDevice::GetFileForEntry(nsCacheEntry *entry, nsIFile **result)
+nsOfflineCacheDevice::GetFileForEntry(nsCacheEntry *entry, nsIFile **result)
 {
-  LOG(("nsDiskCacheDevice::GetFileForEntry [key=%s]\n", entry->Key()->get()));
+  LOG(("nsOfflineCacheDevice::GetFileForEntry [key=%s]\n",
+       entry->Key()->get()));
 
-  nsDiskCacheBinding *binding = (nsDiskCacheBinding *) entry->Data();
+  nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
   NS_ENSURE_STATE(binding);
 
   NS_IF_ADDREF(*result = binding->mDataFile);
   return NS_OK;
 }
 
 nsresult
-nsDiskCacheDevice::OnDataSizeChange(nsCacheEntry *entry, PRInt32 deltaSize)
+nsOfflineCacheDevice::OnDataSizeChange(nsCacheEntry *entry, PRInt32 deltaSize)
 {
-  LOG(("nsDiskCacheDevice::OnDataSizeChange [key=%s delta=%d]\n",
+  LOG(("nsOfflineCacheDevice::OnDataSizeChange [key=%s delta=%d]\n",
       entry->Key()->get(), deltaSize));
 
-  // called to notify us of an impending change in the total size of the
-  // specified entry.  we may wish to trigger an eviction cycle at this point.
+  const PRInt32 DELTA_THRESHOLD = 1<<14; // 16k
 
-  // update the DataSize attribute for this entry
-  /*
+  // called to notify us of an impending change in the total size of the
+  // specified entry.
+
   PRUint32 oldSize = entry->DataSize();
   NS_ASSERTION(deltaSize >= 0 || PRInt32(oldSize) + deltaSize >= 0, "oops");
   PRUint32 newSize = PRInt32(oldSize) + deltaSize;
   UpdateEntrySize(entry, newSize);
-  */
-
-  const PRInt32 DELTA_THRESHOLD = 1<<14; // 16k
 
   mDeltaCounter += deltaSize; // this may go negative
 
-  // run eviction cycle
   if (mDeltaCounter >= DELTA_THRESHOLD)
   {
-    PRUint32 targetCap, delta = mDeltaCounter;
-    if (delta <= mCacheCapacity)
-      targetCap = mCacheCapacity - delta;
-    else
-      targetCap = 0;
-    EvictDiskCacheEntries(targetCap);
+    if (CacheSize() > mCacheCapacity) {
+      // the entry will overrun the cache capacity, doom the entry
+      // and abort
+      nsresult rv = nsCacheService::DoomEntry(entry);
+      NS_ASSERTION(NS_SUCCEEDED(rv), "DoomEntry() failed.");
+      return NS_ERROR_ABORT;
+    }
+
     mDeltaCounter = 0; // reset counter
   }
 
   return NS_OK;
 }
 
 nsresult
-nsDiskCacheDevice::Visit(nsICacheVisitor *visitor)
+nsOfflineCacheDevice::Visit(nsICacheVisitor *visitor)
 {
   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
 
-  // called to enumerate the disk cache.
+  // called to enumerate the offline cache.
 
   nsCOMPtr<nsICacheDeviceInfo> deviceInfo =
-      new nsDiskCacheDeviceInfo(this);
+      new nsOfflineCacheDeviceInfo(this);
 
   PRBool keepGoing;
-  nsresult rv = visitor->VisitDevice(DISK_CACHE_DEVICE_ID, deviceInfo,
+  nsresult rv = visitor->VisitDevice(OFFLINE_CACHE_DEVICE_ID, deviceInfo,
                                      &keepGoing);
   if (NS_FAILED(rv))
     return rv;
   
   if (!keepGoing)
     return NS_OK;
 
   // SELECT * from moz_cache;
 
-  nsDiskCacheRecord rec;
-  nsRefPtr<nsDiskCacheEntryInfo> info = new nsDiskCacheEntryInfo;
+  nsOfflineCacheRecord rec;
+  nsRefPtr<nsOfflineCacheEntryInfo> info = new nsOfflineCacheEntryInfo;
   if (!info)
     return NS_ERROR_OUT_OF_MEMORY;
   info->mRec = &rec;
 
   // XXX may want to list columns explicitly
   nsCOMPtr<mozIStorageStatement> statement;
   rv = mDB->CreateStatement(
       NS_LITERAL_CSTRING("SELECT * FROM moz_cache;"),
@@ -1172,129 +1185,257 @@ nsDiskCacheDevice::Visit(nsICacheVisitor
 
   PRBool hasRows;
   for (;;)
   {
     rv = statement->ExecuteStep(&hasRows);
     if (NS_FAILED(rv) || !hasRows)
       break;
 
-    rec.clientID       = statement->AsSharedCString(0, NULL);
-    rec.key            = statement->AsSharedCString(1, NULL);
-    statement->AsSharedBlob(2, (const void **) &rec.metaData, &rec.metaDataLen);
+    statement->GetSharedUTF8String(0, NULL, &rec.clientID);
+    statement->GetSharedUTF8String(1, NULL, &rec.key);
+    statement->GetSharedBlob(2, &rec.metaDataLen,
+                             (const PRUint8 **) &rec.metaData);
     rec.generation     = statement->AsInt32(3);
     rec.flags          = statement->AsInt32(4);
     rec.dataSize       = statement->AsInt32(5);
     rec.fetchCount     = statement->AsInt32(6);
     rec.lastFetched    = statement->AsInt64(7);
     rec.lastModified   = statement->AsInt64(8);
     rec.expirationTime = statement->AsInt64(9);
 
     PRBool keepGoing;
-    rv = visitor->VisitEntry(DISK_CACHE_DEVICE_ID, info, &keepGoing);
+    rv = visitor->VisitEntry(OFFLINE_CACHE_DEVICE_ID, info, &keepGoing);
     if (NS_FAILED(rv) || !keepGoing)
       break;
   }
 
   info->mRec = nsnull;
   return NS_OK;
 }
 
 nsresult
-nsDiskCacheDevice::EvictEntries(const char *clientID)
+nsOfflineCacheDevice::EvictEntries(const char *clientID)
 {
-  LOG(("nsDiskCacheDevice::EvictEntries [cid=%s]\n", clientID ? clientID : ""));
+  LOG(("nsOfflineCacheDevice::EvictEntries [cid=%s]\n",
+       clientID ? clientID : ""));
 
   // called to evict all entries matching the given clientID.
 
   // need trigger to fire user defined function after a row is deleted
   // so we can delete the corresponding data file.
+  EvictionObserver evictionObserver(mDB);
 
-  nsresult rv = EnableEvictionObserver();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // hook up our eviction observer
-  
   const char *deleteCmd;
   if (clientID)
   {
     deleteCmd =
       PR_smprintf("DELETE FROM moz_cache WHERE ClientID=%q AND Flags=0;",
                   clientID);
     if (!deleteCmd)
       return NS_ERROR_OUT_OF_MEMORY;
   }
   else
   {
     deleteCmd = "DELETE FROM moz_cache WHERE Flags = 0;";
   }
 
-  rv = mDB->ExecuteSimpleSQL(nsDependentCString(deleteCmd));
+  nsresult rv = mDB->ExecuteSimpleSQL(nsDependentCString(deleteCmd));
   if (clientID)
     PR_smprintf_free((char *) deleteCmd);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  DisableEvictionObserver();
-
   return NS_OK;
 }
 
 nsresult
-nsDiskCacheDevice::EvictDiskCacheEntries(PRUint32 desiredCapacity)
+nsOfflineCacheDevice::SetOwnedKeys(const char * clientID,
+                                   const nsACString & ownerDomain,
+                                   const nsACString & ownerURI,
+                                   PRUint32 count,
+                                   const char ** keys)
 {
-  LOG(("nsDiskCacheDevice::EvictDiskCacheEntries [goal=%u delta=%d]\n",
-      desiredCapacity, mCacheCapacity - desiredCapacity));
+  LOG(("nsOfflineCacheDevice::SetOwnedKeys [cid=%s]\n", clientID));
+  mozStorageTransaction transaction(mDB, PR_FALSE);
 
-  // need trigger to fire user defined function after a row is deleted
-  // so we can delete the corresponding data file.
+  nsDependentCString clientIDStr(clientID);
 
-  // BEGIN
-  // while ("SELECT Sum(DataSize) FROM moz_cache;" > desiredCapacity)
-  //   DELETE FROM moz_cache WHERE Min(LastFetched);
-  // END
+  AutoResetStatement clearStatement(mStatement_ClearOwnership);
+  nsresult rv = clearStatement->BindUTF8StringParameter(
+                                               0, clientIDStr);
+  rv |= clearStatement->BindUTF8StringParameter(1, ownerDomain);
+  rv |= clearStatement->BindUTF8StringParameter(2, ownerURI);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  nsresult rv = EnableEvictionObserver();
+  rv = clearStatement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRUint32 lastCacheSize = PR_UINT32_MAX, cacheSize;
-  for (;;)
+  for (PRUint32 i = 0; i < count; i++)
   {
-    cacheSize = CacheSize();
-    if (cacheSize <= desiredCapacity)
-      break;
-    if (cacheSize == lastCacheSize)
-    {
-      LOG(("unable to reduce cache size to target capacity!\n"));
-      break;
-    }
+    AutoResetStatement insertStatement(mStatement_AddOwnership);
+    rv = insertStatement->BindUTF8StringParameter(0, clientIDStr);
+    rv |= insertStatement->BindUTF8StringParameter(1, ownerDomain);
+    rv |= insertStatement->BindUTF8StringParameter(2, ownerURI);
+    rv |= insertStatement->BindUTF8StringParameter(3, nsDependentCString(keys[i]));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = mDB->ExecuteSimpleSQL(
-        NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE LastFetched IN ("
-                           " SELECT Min(LastFetched) FROM moz_cache"
-                           " WHERE Flags=0);"));
-    if (NS_FAILED(rv))
-    {
-      LOG(("failure while deleting Min(LastFetched)\n"));
-      break;
-    }
-
-    lastCacheSize = cacheSize;
+    rv = insertStatement->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  DisableEvictionObserver();
+  return transaction.Commit();
+}
+
+nsresult
+nsOfflineCacheDevice::GetOwnedKeys(const char * clientID,
+                                   const nsACString & ownerDomain,
+                                   const nsACString & ownerURI,
+                                   PRUint32 * count,
+                                   char *** keys)
+{
+  LOG(("nsOfflineCacheDevice::GetOwnedKeys [cid=%s]\n", clientID));
+
+  AutoResetStatement statement(mStatement_ListOwned);
+  nsresult rv = statement->BindUTF8StringParameter(
+                                           0, nsDependentCString(clientID));
+  rv |= statement->BindUTF8StringParameter(1, ownerDomain);
+  rv |= statement->BindUTF8StringParameter(2, ownerURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRBool hasRows;
+  rv = statement->ExecuteStep(&hasRows);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsTArray<nsCString> keyArray;
+  while (hasRows)
+  {
+    PRUint32 length;
+    keyArray.AppendElement(
+      nsDependentCString(statement->AsSharedUTF8String(0, &length)));
+
+    rv = statement->ExecuteStep(&hasRows);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  *count = keyArray.Length();
+  char **ret = NS_STATIC_CAST(char **, NS_Alloc(*count * sizeof(char*)));
+  if (!ret) return NS_ERROR_OUT_OF_MEMORY;
+
+  for (PRUint32 i = 0; i <  *count; i++) {
+    ret[i] = NS_strdup(keyArray[i].get());
+    if (!ret[i]) {
+      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  *keys = ret;
+
   return NS_OK;
 }
 
+nsresult
+nsOfflineCacheDevice::AddOwnedKey(const char * clientID,
+                                  const nsACString & ownerDomain,
+                                  const nsACString & ownerURI,
+                                  const nsACString & key)
+{
+  LOG(("nsOfflineCacheDevice::AddOwnedKey [cid=%s]\n", clientID));
+
+  PRBool isOwned;
+  nsresult rv = KeyIsOwned(clientID, ownerDomain, ownerURI, key, &isOwned);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (isOwned) return NS_OK;
+
+  AutoResetStatement statement(mStatement_AddOwnership);
+  rv = statement->BindUTF8StringParameter(0, nsDependentCString(clientID));
+  rv |= statement->BindUTF8StringParameter(1, ownerDomain);
+  rv |= statement->BindUTF8StringParameter(2, ownerURI);
+  rv |= statement->BindUTF8StringParameter(3, key);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return statement->Execute();
+}
+
+nsresult
+nsOfflineCacheDevice::RemoveOwnedKey(const char * clientID,
+                                     const nsACString & ownerDomain,
+                                     const nsACString & ownerURI,
+                                     const nsACString & key)
+{
+  LOG(("nsOfflineCacheDevice::RemoveOwnedKey [cid=%s]\n", clientID));
+
+  PRBool isOwned;
+  nsresult rv = KeyIsOwned(clientID, ownerDomain, ownerURI, key, &isOwned);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!isOwned) return NS_ERROR_NOT_AVAILABLE;
+
+  AutoResetStatement statement(mStatement_RemoveOwnership);
+  rv = statement->BindUTF8StringParameter(0, nsDependentCString(clientID));
+  rv |= statement->BindUTF8StringParameter(1, ownerDomain);
+  rv |= statement->BindUTF8StringParameter(2, ownerURI);
+  rv |= statement->BindUTF8StringParameter(3, key);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return statement->Execute();
+}
+
+nsresult
+nsOfflineCacheDevice::KeyIsOwned(const char * clientID,
+                                 const nsACString & ownerDomain,
+                                 const nsACString & ownerURI,
+                                 const nsACString & key,
+                                 PRBool * isOwned)
+{
+  AutoResetStatement statement(mStatement_CheckOwnership);
+  nsresult rv = statement->BindUTF8StringParameter(
+                                           0, nsDependentCString(clientID));
+  rv |= statement->BindUTF8StringParameter(1, ownerDomain);
+  rv |= statement->BindUTF8StringParameter(2, ownerURI);
+  rv |= statement->BindUTF8StringParameter(3, key);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return statement->ExecuteStep(isOwned);
+}
+
+nsresult
+nsOfflineCacheDevice::ClearKeysOwnedByDomain(const char *clientID,
+                                             const nsACString &domain)
+{
+  LOG(("nsOfflineCacheDevice::ClearKeysOwnedByDomain [cid=%s]\n", clientID));
+
+  AutoResetStatement statement(mStatement_ClearDomain);
+  nsresult rv = statement->BindUTF8StringParameter(
+                                           0, nsDependentCString(clientID));
+  rv |= statement->BindUTF8StringParameter(1, domain);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return statement->Execute();
+}
+
+nsresult
+nsOfflineCacheDevice::EvictUnownedEntries(const char *clientID)
+{
+  LOG(("nsOfflineCacheDevice::EvictUnownedEntries [cid=%s]\n", clientID));
+  EvictionObserver evictionObserver(mDB);
+
+  AutoResetStatement statement(mStatement_DeleteUnowned);
+  nsresult rv = statement->BindUTF8StringParameter(
+                                              0, nsDependentCString(clientID));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return statement->Execute();
+}
 
 /**
  * Preference accessors
  */
 
 void
-nsDiskCacheDevice::SetCacheParentDirectory(nsILocalFile *parentDir)
+nsOfflineCacheDevice::SetCacheParentDirectory(nsILocalFile *parentDir)
 {
   if (Initialized())
   {
     NS_ERROR("cannot switch cache directory once initialized");
     return;
   }
 
   if (!parentDir)
@@ -1311,22 +1452,20 @@ nsDiskCacheDevice::SetCacheParentDirecto
     return;
   }
 
   // cache dir may not exist, but that's ok
   nsCOMPtr<nsIFile> dir;
   rv = parentDir->Clone(getter_AddRefs(dir));
   if (NS_FAILED(rv))
     return;
-  rv = dir->AppendNative(NS_LITERAL_CSTRING("cache_sql"));
+  rv = dir->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
   if (NS_FAILED(rv))
     return;
 
   mCacheDirectory = do_QueryInterface(dir);
 }
 
 void
-nsDiskCacheDevice::SetCapacity(PRUint32 capacity)
+nsOfflineCacheDevice::SetCapacity(PRUint32 capacity)
 {
   mCacheCapacity = capacity * 1024;
-  if (Initialized())
-    EvictDiskCacheEntries(mCacheCapacity);
 }
--- a/netwerk/cache/src/nsDiskCacheDeviceSQL.h
+++ b/netwerk/cache/src/nsDiskCacheDeviceSQL.h
@@ -30,35 +30,36 @@
  * 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 nsDiskCacheDeviceSQL_h__
-#define nsDiskCacheDeviceSQL_h__
+#ifndef nsOfflineCacheDevice_h__
+#define nsOfflineCacheDevice_h__
 
 #include "nsCacheDevice.h"
 #include "nsILocalFile.h"
 #include "nsIObserver.h"
 #include "mozIStorageConnection.h"
 #include "nsCOMPtr.h"
+#include "nsVoidArray.h"
 
-class nsDiskCacheDevice : public nsCacheDevice
+class nsOfflineCacheDevice : public nsCacheDevice
 {
 public:
-  nsDiskCacheDevice();
+  nsOfflineCacheDevice();
 
   /**
    * nsCacheDevice methods
    */
- 
-  virtual ~nsDiskCacheDevice();
+
+  virtual ~nsOfflineCacheDevice();
 
   virtual nsresult        Init();
   virtual nsresult        Shutdown();
 
   virtual const char *    GetDeviceID(void);
   virtual nsCacheEntry *  FindEntry(nsCString * key, PRBool *collision);
   virtual nsresult        DeactivateEntry(nsCacheEntry * entry);
   virtual nsresult        BindEntry(nsCacheEntry * entry);
@@ -79,51 +80,82 @@ public:
 
   virtual nsresult        OnDataSizeChange(nsCacheEntry * entry, PRInt32 deltaSize);
   
   virtual nsresult        Visit(nsICacheVisitor * visitor);
 
   virtual nsresult        EvictEntries(const char * clientID);
 
 
+  /* Entry ownership */
+  nsresult                SetOwnedKeys(const char *           clientID,
+                                       const nsACString &     ownerDomain,
+                                       const nsACString &     ownerUrl,
+                                       PRUint32               count,
+                                       const char **          keys);
+  nsresult                GetOwnedKeys(const char *           clientID,
+                                       const nsACString &     ownerDomain,
+                                       const nsACString &     ownerUrl,
+                                       PRUint32 *             count,
+                                       char ***               keys);
+  nsresult                AddOwnedKey(const char *            clientID,
+                                      const nsACString &      ownerDomain,
+                                      const nsACString &      ownerURI,
+                                      const nsACString &      key);
+  nsresult                RemoveOwnedKey(const char *         clientID,
+                                         const nsACString &   ownerDomain,
+                                         const nsACString &   ownerURI,
+                                         const nsACString &   key);
+  nsresult                KeyIsOwned(const char *             clientID,
+                                     const nsACString &       ownerDomain,
+                                     const nsACString &       ownerURI,
+                                     const nsACString &       key,
+                                     PRBool *                 isOwned);
+
+  nsresult                ClearKeysOwnedByDomain(const char *clientID,
+                                                 const nsACString &ownerDomain);
+  nsresult                EvictUnownedEntries(const char *clientID);
+
+
   /**
    * Preference accessors
    */
 
   void                    SetCacheParentDirectory(nsILocalFile * parentDir);
   void                    SetCapacity(PRUint32  capacity);
 
   nsILocalFile *          CacheDirectory() { return mCacheDirectory; }
   PRUint32                CacheCapacity() { return mCacheCapacity; }
   PRUint32                CacheSize();
   PRUint32                EntryCount();
   
-
-private:    
+private:
   PRBool   Initialized() { return mDB != nsnull; }
-  nsresult EvictDiskCacheEntries(PRUint32 targetCapacity);
   nsresult UpdateEntry(nsCacheEntry *entry);
   nsresult UpdateEntrySize(nsCacheEntry *entry, PRUint32 newSize);
   nsresult DeleteEntry(nsCacheEntry *entry, PRBool deleteData);
   nsresult DeleteData(nsCacheEntry *entry);
   nsresult EnableEvictionObserver();
   nsresult DisableEvictionObserver();
 
-#if 0
-  // sqlite function for observing DELETE events
-  static void EvictionObserver(struct sqlite3_context *, int, struct Mem **);
-#endif
-
   nsCOMPtr<mozIStorageConnection> mDB;
   nsCOMPtr<mozIStorageStatement>  mStatement_CacheSize;
   nsCOMPtr<mozIStorageStatement>  mStatement_EntryCount;
   nsCOMPtr<mozIStorageStatement>  mStatement_UpdateEntry;
   nsCOMPtr<mozIStorageStatement>  mStatement_UpdateEntrySize;
+  nsCOMPtr<mozIStorageStatement>  mStatement_UpdateEntryFlags;
   nsCOMPtr<mozIStorageStatement>  mStatement_DeleteEntry;
   nsCOMPtr<mozIStorageStatement>  mStatement_FindEntry;
   nsCOMPtr<mozIStorageStatement>  mStatement_BindEntry;
+  nsCOMPtr<mozIStorageStatement>  mStatement_ClearOwnership;
+  nsCOMPtr<mozIStorageStatement>  mStatement_RemoveOwnership;
+  nsCOMPtr<mozIStorageStatement>  mStatement_ClearDomain;
+  nsCOMPtr<mozIStorageStatement>  mStatement_AddOwnership;
+  nsCOMPtr<mozIStorageStatement>  mStatement_CheckOwnership;
+  nsCOMPtr<mozIStorageStatement>  mStatement_DeleteUnowned;
+  nsCOMPtr<mozIStorageStatement>  mStatement_ListOwned;
 
   nsCOMPtr<nsILocalFile>          mCacheDirectory;
-  PRUint32                        mCacheCapacity;     // XXX need soft/hard limits, currentTotal
+  PRUint32                        mCacheCapacity;
   PRInt32                         mDeltaCounter;
 };
 
-#endif // nsDiskCacheDeviceSQL_h__
+#endif // nsOfflineCacheDevice_h__
--- a/netwerk/cache/src/nsDiskCacheEntry.cpp
+++ b/netwerk/cache/src/nsDiskCacheEntry.cpp
@@ -138,17 +138,17 @@ NS_IMETHODIMP nsDiskCacheEntryInfo::GetC
     NS_ENSURE_ARG_POINTER(clientID);
     return ClientIDFromCacheKey(nsDependentCString(mDiskEntry->Key()), clientID);
 }
 
 extern const char DISK_CACHE_DEVICE_ID[];
 NS_IMETHODIMP nsDiskCacheEntryInfo::GetDeviceID(char ** deviceID)
 {
     NS_ENSURE_ARG_POINTER(deviceID);
-    *deviceID = nsCRT::strdup(mDeviceID);
+    *deviceID = NS_strdup(mDeviceID);
     return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
 }
 
 
 NS_IMETHODIMP nsDiskCacheEntryInfo::GetKey(nsACString &clientKey)
 {
     return ClientKeyFromCacheKey(nsDependentCString(mDiskEntry->Key()), clientKey);
 }
--- a/netwerk/cache/src/nsMemoryCacheDevice.cpp
+++ b/netwerk/cache/src/nsMemoryCacheDevice.cpp
@@ -516,17 +516,17 @@ nsMemoryCacheDevice::CheckEntryCount()
 
 NS_IMPL_ISUPPORTS1(nsMemoryCacheDeviceInfo, nsICacheDeviceInfo)
 
 
 NS_IMETHODIMP
 nsMemoryCacheDeviceInfo::GetDescription(char ** result)
 {
     NS_ENSURE_ARG_POINTER(result);
-    *result = nsCRT::strdup("Memory cache device");
+    *result = NS_strdup("Memory cache device");
     if (!*result) return NS_ERROR_OUT_OF_MEMORY;
     return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsMemoryCacheDeviceInfo::GetUsageReport(char ** result)
 {
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -31,16 +31,19 @@
  * 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 "nsPrefetchService.h"
+#include "nsICacheSession.h"
+#include "nsIOfflineCacheSession.h"
+#include "nsICacheService.h"
 #include "nsIServiceManager.h"
 #include "nsICategoryManager.h"
 #include "nsIObserverService.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch2.h"
 #include "nsIDocCharset.h"
 #include "nsIWebProgress.h"
 #include "nsCURILoader.h"
@@ -67,19 +70,16 @@
 // this enables PR_LOG_ALWAYS level information and places all output in
 // the file http.log
 //
 static PRLogModuleInfo *gPrefetchLog;
 #endif
 #define LOG(args) PR_LOG(gPrefetchLog, 4, args)
 #define LOG_ENABLED() PR_LOG_TEST(gPrefetchLog, 4)
 
-static NS_DEFINE_IID(kDocLoaderServiceCID, NS_DOCUMENTLOADER_SERVICE_CID);
-static NS_DEFINE_IID(kPrefServiceCID, NS_PREFSERVICE_CID);
-
 #define PREFETCH_PREF "network.prefetch-next"
 
 //-----------------------------------------------------------------------------
 // helpers
 //-----------------------------------------------------------------------------
 
 static inline PRUint32
 PRTimeToSeconds(PRTime t_usec)
@@ -280,16 +280,17 @@ nsPrefetchListener::OnChannelRedirect(ns
 // nsPrefetchService <public>
 //-----------------------------------------------------------------------------
 
 nsPrefetchService::nsPrefetchService()
     : mQueueHead(nsnull)
     , mQueueTail(nsnull)
     , mStopCount(0)
     , mDisabled(PR_TRUE)
+    , mFetchedOffline(PR_FALSE)
 {
 }
 
 nsPrefetchService::~nsPrefetchService()
 {
     // cannot reach destructor if prefetch in progress (listener owns reference
     // to this service)
     EmptyQueue(PR_TRUE);
@@ -301,17 +302,17 @@ nsPrefetchService::Init()
 #if defined(PR_LOGGING)
     if (!gPrefetchLog)
         gPrefetchLog = PR_NewLogModule("nsPrefetch");
 #endif
 
     nsresult rv;
 
     // read prefs and hook up pref observer
-    nsCOMPtr<nsIPrefBranch2> prefs(do_GetService(kPrefServiceCID, &rv));
+    nsCOMPtr<nsIPrefBranch2> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
     if (NS_SUCCEEDED(rv)) {
       PRBool enabled;
       rv = prefs->GetBoolPref(PREFETCH_PREF, &enabled);
       if (NS_SUCCEEDED(rv) && enabled)
         mDisabled = PR_FALSE;
 
       prefs->AddObserver(PREFETCH_PREF, this, PR_TRUE);
     }
@@ -340,16 +341,29 @@ nsPrefetchService::ProcessNextURI()
 
     nsRefPtr<nsPrefetchListener> listener(new nsPrefetchListener(this));
     if (!listener) return;
 
     do {
         PRBool offline;
         rv = DequeueURI(getter_AddRefs(uri), getter_AddRefs(referrer),
                         &offline);
+        if (rv == NS_ERROR_NOT_AVAILABLE && mFetchedOffline) {
+            // done loading stuff, go ahead and evict unowned entries from
+            // the offline cache
+            mFetchedOffline = PR_FALSE;
+
+            nsCOMPtr<nsIOfflineCacheSession> session;
+            rv = GetOfflineCacheSession(getter_AddRefs(session));
+            if (NS_FAILED(rv)) break;
+
+            session->EvictUnownedEntries();
+            break;
+        }
+
         if (NS_FAILED(rv)) break;
 
 #if defined(PR_LOGGING)
         if (LOG_ENABLED()) {
             nsCAutoString spec;
             uri->GetSpec(spec);
             LOG(("ProcessNextURI [%s]\n", spec.get()));
         }
@@ -380,41 +394,45 @@ nsPrefetchService::ProcessNextURI()
         if (offline) {
             nsCOMPtr<nsICachingChannel> cachingChannel =
                 do_QueryInterface(mCurrentChannel);
             if (cachingChannel) {
                 if (NS_FAILED(cachingChannel->SetCacheForOfflineUse(PR_TRUE))) {
                     continue;
                 }
             }
+
+            mFetchedOffline = PR_TRUE;
         }
 
         rv = mCurrentChannel->AsyncOpen(listener, nsnull);
     }
     while (NS_FAILED(rv));
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService <private>
 //-----------------------------------------------------------------------------
 
 void
 nsPrefetchService::AddProgressListener()
 {
     // Register as an observer for the document loader  
-    nsCOMPtr<nsIWebProgress> progress(do_GetService(kDocLoaderServiceCID));
+    nsCOMPtr<nsIWebProgress> progress = 
+        do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
     if (progress)
         progress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
 }
 
 void
 nsPrefetchService::RemoveProgressListener()
 {
     // Register as an observer for the document loader  
-    nsCOMPtr<nsIWebProgress> progress(do_GetService(kDocLoaderServiceCID));
+    nsCOMPtr<nsIWebProgress> progress =
+        do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
     if (progress)
         progress->RemoveProgressListener(this);
 }
 
 nsresult
 nsPrefetchService::EnqueueURI(nsIURI *aURI,
                               nsIURI *aReferrerURI,
                               PRBool aOffline)
@@ -475,16 +493,41 @@ nsPrefetchService::EmptyQueue(PRBool inc
         }
         else
             prev = node;
 
         node = next;
     }
 }
 
+nsresult
+nsPrefetchService::GetOfflineCacheSession(nsIOfflineCacheSession **aSession)
+{
+    if (!mOfflineCacheSession) {
+        nsresult rv;
+        nsCOMPtr<nsICacheService> serv =
+            do_GetService(NS_CACHESERVICE_CONTRACTID,
+                          &rv);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        nsCOMPtr<nsICacheSession> session;
+        rv = serv->CreateSession("HTTP-offline",
+                                 nsICache::STORE_OFFLINE,
+                                 nsICache::STREAM_BASED,
+                                 getter_AddRefs(session));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mOfflineCacheSession = do_QueryInterface(session, &rv);
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    NS_ADDREF(*aSession = mOfflineCacheSession);
+    return NS_OK;
+}
+
 void
 nsPrefetchService::StartPrefetching()
 {
     //
     // at initialization time we might miss the first DOCUMENT START
     // notification, so we have to be careful to avoid letting our
     // stop count go negative.
     //
--- a/uriloader/prefetch/nsPrefetchService.h
+++ b/uriloader/prefetch/nsPrefetchService.h
@@ -42,22 +42,24 @@
 #include "nsIGenericFactory.h"
 #include "nsIObserver.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIChannelEventSink.h"
 #include "nsIWebProgressListener.h"
 #include "nsIStreamListener.h"
 #include "nsIChannel.h"
 #include "nsIURI.h"
+#include "nsIDOMDocument.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 
 class nsPrefetchService;
 class nsPrefetchListener;
 class nsPrefetchNode;
+class nsIOfflineCacheSession;
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService
 //-----------------------------------------------------------------------------
 
 class nsPrefetchService : public nsIPrefetchService
                         , public nsIWebProgressListener
                         , public nsIObserver
@@ -83,24 +85,31 @@ private:
                       PRBool aExplicit,
                       PRBool aOffline);
 
     void     AddProgressListener();
     void     RemoveProgressListener();
     nsresult EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI, PRBool aOffline);
     nsresult DequeueURI(nsIURI **aURI, nsIURI **aReferrerURI, PRBool *aOffline);
     void     EmptyQueue(PRBool includeOffline);
+    nsresult SaveOfflineList(nsIURI *aDocumentUri,
+                             nsIDOMDocument *aDoc);
+    nsresult GetOfflineCacheSession(nsIOfflineCacheSession **aSession);
+
     void     StartPrefetching();
     void     StopPrefetching();
 
-    nsPrefetchNode      *mQueueHead;
-    nsPrefetchNode      *mQueueTail;
-    nsCOMPtr<nsIChannel> mCurrentChannel;
-    PRInt32              mStopCount;
-    PRBool               mDisabled;
+    nsCOMPtr<nsIOfflineCacheSession>  mOfflineCacheSession;
+    nsPrefetchNode                   *mQueueHead;
+    nsPrefetchNode                   *mQueueTail;
+    nsCOMPtr<nsIChannel>              mCurrentChannel;
+    PRInt32                           mStopCount;
+    PRBool                            mDisabled;
+    PRBool                            mFetchedOffline;
+
 };
 
 //-----------------------------------------------------------------------------
 // nsPrefetchListener
 //-----------------------------------------------------------------------------
 
 class nsPrefetchListener : public nsIStreamListener
                          , public nsIInterfaceRequestor