Backed out changeset bbaf0d5fef61 (bug 442803)
authorDave Camp <dcamp@mozilla.com>
Tue, 19 Aug 2008 22:54:37 -0700
changeset 17127 2053bab906dc548240c7d3e279af04ffcac48843
parent 17089 bbaf0d5fef61d03cb6fbb41f334eeccb6e48599f
child 17128 ddbef216a633a24de625b91684a786262fc0b492
push idunknown
push userunknown
push dateunknown
bugs442803
milestone1.9.1a2pre
backs outbbaf0d5fef61d03cb6fbb41f334eeccb6e48599f
Backed out changeset bbaf0d5fef61 (bug 442803)
uriloader/prefetch/nsIPrefetchService.idl
uriloader/prefetch/nsPrefetchService.cpp
uriloader/prefetch/nsPrefetchService.h
--- a/uriloader/prefetch/nsIPrefetchService.idl
+++ b/uriloader/prefetch/nsIPrefetchService.idl
@@ -54,32 +54,35 @@ interface nsIPrefetchService : nsISuppor
      * @param aExplicit the link element has an explicit prefetch link type
      */
     void prefetchURI(in nsIURI aURI,
                      in nsIURI aReferrerURI,
                      in nsIDOMNode aSource,
                      in boolean aExplicit);
 
     /**
-     * @status DEPRECATED This method is no longer used, and will throw
-     * NS_ERROR_NOT_IMPLEMENTED.
+     * Enqueue a request to prefetch the specified URI, making sure it's in the
+     * offline cache.
+     *
+     * @param aURI the URI of the document to prefetch
+     * @param aReferrerURI the URI of the referring page
+     * @param aSource the DOM node (such as a <link> tag) that requested this
+     *        fetch, or null if the prefetch was not requested by a DOM node.
+     * @param aExplicit the link element has an explicit offline link type
      */
     void prefetchURIForOfflineUse(in nsIURI aURI,
                                   in nsIURI aReferrerURI,
                                   in nsIDOMNode aSource,
                                   in boolean aExplicit);
 
     /**
      * Enumerate the items in the prefetch queue.  Each element in the
      * enumeration is an nsIDOMLoadStatus.
      *
-     * @param aIncludeNormalItems include normal prefetch items in the
-     *        list.  This parameter is deprecated and must be TRUE,
-     *        or NS_ERROR_INT_IMPLEMENTED will be thrown.
+     * @param aIncludeNormalItems include normal prefetch items in the list.
      * @param aIncludeOfflineItems include items being fetched for offline
-     *        use.  This parameter is deprecated and must be FALSE,
-     *        or NS_ERROR_NOT_IMPLEMENTED will be thrown.
+     *        use.
      */
     nsISimpleEnumerator enumerateQueue(in boolean aIncludeNormalItems,
                                        in boolean aIncludeOfflineItems);
 
     // XXX do we need a way to cancel prefetch requests?
 };
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -32,16 +32,17 @@
  * 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"
@@ -98,32 +99,40 @@ PRTimeToSeconds(PRTime t_usec)
 //-----------------------------------------------------------------------------
 // nsPrefetchQueueEnumerator
 //-----------------------------------------------------------------------------
 class nsPrefetchQueueEnumerator : public nsISimpleEnumerator
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSISIMPLEENUMERATOR
-    nsPrefetchQueueEnumerator(nsPrefetchService *aService);
+    nsPrefetchQueueEnumerator(nsPrefetchService *aService,
+                              PRBool aIncludeNormal,
+                              PRBool aIncludeOffline);
     ~nsPrefetchQueueEnumerator();
 
 private:
     void Increment();
 
     nsRefPtr<nsPrefetchService> mService;
     nsRefPtr<nsPrefetchNode> mCurrent;
+    PRBool mIncludeNormal;
+    PRBool mIncludeOffline;
     PRBool mStarted;
 };
 
 //-----------------------------------------------------------------------------
 // nsPrefetchQueueEnumerator <public>
 //-----------------------------------------------------------------------------
-nsPrefetchQueueEnumerator::nsPrefetchQueueEnumerator(nsPrefetchService *aService)
+nsPrefetchQueueEnumerator::nsPrefetchQueueEnumerator(nsPrefetchService *aService,
+                                                     PRBool aIncludeNormal,
+                                                     PRBool aIncludeOffline)
     : mService(aService)
+    , mIncludeNormal(aIncludeNormal)
+    , mIncludeOffline(aIncludeOffline)
     , mStarted(PR_FALSE)
 {
     Increment();
 }
 
 nsPrefetchQueueEnumerator::~nsPrefetchQueueEnumerator()
 {
 }
@@ -173,36 +182,39 @@ nsPrefetchQueueEnumerator::Increment()
                 // start with the pending queue
                 mCurrent = mService->GetQueueHead();
             }
             else {
                 // Otherwise just advance to the next item in the queue
                 mCurrent = mCurrent->mNext;
             }
         }
-    } while (mCurrent);
+    } while (mCurrent && mIncludeOffline != mCurrent->mOffline &&
+             mIncludeNormal == mCurrent->mOffline);
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchQueueEnumerator::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS1(nsPrefetchQueueEnumerator, nsISimpleEnumerator)
 
 //-----------------------------------------------------------------------------
 // nsPrefetchNode <public>
 //-----------------------------------------------------------------------------
 
 nsPrefetchNode::nsPrefetchNode(nsPrefetchService *aService,
                                nsIURI *aURI,
                                nsIURI *aReferrerURI,
-                               nsIDOMNode *aSource)
+                               nsIDOMNode *aSource,
+                               PRBool aOffline)
     : mNext(nsnull)
     , mURI(aURI)
     , mReferrerURI(aReferrerURI)
+    , mOffline(aOffline)
     , mService(aService)
     , mChannel(nsnull)
     , mState(nsIDOMLoadStatus::UNINITIALIZED)
     , mBytesRead(0)
 {
     mSource = do_GetWeakReference(aSource);
 }
 
@@ -218,20 +230,31 @@ nsPrefetchNode::OpenChannel()
 
     // configure HTTP specific stuff
     nsCOMPtr<nsIHttpChannel> httpChannel =
         do_QueryInterface(mChannel);
     if (httpChannel) {
         httpChannel->SetReferrer(mReferrerURI);
         httpChannel->SetRequestHeader(
             NS_LITERAL_CSTRING("X-Moz"),
+            mOffline ?
+            NS_LITERAL_CSTRING("offline-resource") :
             NS_LITERAL_CSTRING("prefetch"),
             PR_FALSE);
     }
 
+    if (mOffline) {
+        nsCOMPtr<nsICachingChannel> cachingChannel =
+            do_QueryInterface(mChannel);
+        if (cachingChannel) {
+            rv = cachingChannel->SetCacheForOfflineUse(PR_TRUE);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
+    }
+
     rv = mChannel->AsyncOpen(this, nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mState = nsIDOMLoadStatus::REQUESTED;
 
     return NS_OK;
 }
 
@@ -266,44 +289,50 @@ nsPrefetchNode::OnStartRequest(nsIReques
                                nsISupports *aContext)
 {
     nsresult rv;
 
     nsCOMPtr<nsICachingChannel> cachingChannel =
         do_QueryInterface(aRequest, &rv);
     if (NS_FAILED(rv)) return rv;
 
-    // no need to prefetch a document that is already in the cache
-    PRBool fromCache;
-    if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) &&
-        fromCache) {
-        LOG(("document is already in the cache; canceling prefetch\n"));
-        return NS_BINDING_ABORTED;
-    }
-
-    //
-    // no need to prefetch a document that must be requested fresh each
-    // and every time.
-    //
-    nsCOMPtr<nsISupports> cacheToken;
-    cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
-    if (!cacheToken)
-        return NS_ERROR_ABORT; // bail, no cache entry
-
-    nsCOMPtr<nsICacheEntryInfo> entryInfo =
-        do_QueryInterface(cacheToken, &rv);
+    PRBool cacheForOfflineUse;
+    rv = cachingChannel->GetCacheForOfflineUse(&cacheForOfflineUse);
     if (NS_FAILED(rv)) return rv;
 
-    PRUint32 expTime;
-    if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) {
-        if (NowInSeconds() >= expTime) {
-            LOG(("document cannot be reused from cache; "
-                 "canceling prefetch\n"));
+    if (!cacheForOfflineUse) {
+        // no need to prefetch a document that is already in the cache
+        PRBool fromCache;
+        if (NS_SUCCEEDED(cachingChannel->IsFromCache(&fromCache)) &&
+            fromCache) {
+            LOG(("document is already in the cache; canceling prefetch\n"));
             return NS_BINDING_ABORTED;
         }
+
+        //
+        // no need to prefetch a document that must be requested fresh each
+        // and every time.
+        //
+        nsCOMPtr<nsISupports> cacheToken;
+        cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
+        if (!cacheToken)
+            return NS_ERROR_ABORT; // bail, no cache entry
+
+        nsCOMPtr<nsICacheEntryInfo> entryInfo =
+            do_QueryInterface(cacheToken, &rv);
+        if (NS_FAILED(rv)) return rv;
+
+        PRUint32 expTime;
+        if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expTime))) {
+            if (NowInSeconds() >= expTime) {
+                LOG(("document cannot be reused from cache; "
+                     "canceling prefetch\n"));
+                return NS_BINDING_ABORTED;
+            }
+        }
     }
 
     mState = nsIDOMLoadStatus::RECEIVING;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -367,32 +396,46 @@ nsPrefetchNode::OnChannelRedirect(nsICha
                                   nsIChannel *aNewChannel,
                                   PRUint32 aFlags)
 {
     nsCOMPtr<nsIURI> newURI;
     nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
     if (NS_FAILED(rv))
         return rv;
 
+    PRBool offline;
     nsCOMPtr<nsICachingChannel> oldCachingChannel =
         do_QueryInterface(aOldChannel);
+    if (NS_SUCCEEDED(oldCachingChannel->GetCacheForOfflineUse(&offline)) &&
+        offline) {
+        nsCOMPtr<nsICachingChannel> newCachingChannel =
+            do_QueryInterface(aOldChannel);
+        if (newCachingChannel)
+            newCachingChannel->SetCacheForOfflineUse(PR_TRUE);
+    }
 
     PRBool match;
     rv = newURI->SchemeIs("http", &match); 
     if (NS_FAILED(rv) || !match) {
-        LOG(("rejected: URL is not of type http\n"));
-        return NS_ERROR_ABORT;
+        if (!offline ||
+            NS_FAILED(newURI->SchemeIs("https", &match)) ||
+            !match) {
+            LOG(("rejected: URL is not of type http\n"));
+            return NS_ERROR_ABORT;
+        }
     }
 
     // HTTP request headers are not automatically forwarded to the new channel.
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
     NS_ENSURE_STATE(httpChannel);
 
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
-                                  NS_LITERAL_CSTRING("prefetch"),
+                                  offline ?
+                                      NS_LITERAL_CSTRING("offline-resource") :
+                                      NS_LITERAL_CSTRING("prefetch"),
                                   PR_FALSE);
 
     mChannel = aNewChannel;
 
     return NS_OK;
 }
 
 
@@ -401,24 +444,25 @@ nsPrefetchNode::OnChannelRedirect(nsICha
 //-----------------------------------------------------------------------------
 
 nsPrefetchService::nsPrefetchService()
     : mQueueHead(nsnull)
     , mQueueTail(nsnull)
     , mStopCount(0)
     , mHaveProcessed(PR_FALSE)
     , mDisabled(PR_TRUE)
+    , mFetchedOffline(PR_FALSE)
 {
 }
 
 nsPrefetchService::~nsPrefetchService()
 {
     // cannot reach destructor if prefetch in progress (listener owns reference
     // to this service)
-    EmptyQueue();
+    EmptyQueue(PR_TRUE);
 }
 
 nsresult
 nsPrefetchService::Init()
 {
 #if defined(PR_LOGGING)
     if (!gPrefetchLog)
         gPrefetchLog = PR_NewLogModule("nsPrefetch");
@@ -456,59 +500,79 @@ nsPrefetchService::ProcessNextURI()
 {
     nsresult rv;
     nsCOMPtr<nsIURI> uri, referrer;
 
     mCurrentNode = nsnull;
 
     do {
         rv = DequeueNode(getter_AddRefs(mCurrentNode));
+        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;
             mCurrentNode->mURI->GetSpec(spec);
             LOG(("ProcessNextURI [%s]\n", spec.get()));
         }
 #endif
 
         //
         // if opening the channel fails, then just skip to the next uri
         //
         rv = mCurrentNode->OpenChannel();
+        if (NS_SUCCEEDED(rv) && mCurrentNode->mOffline)
+            mFetchedOffline = PR_TRUE;
     }
     while (NS_FAILED(rv));
 }
 
 void
 nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node)
 {
     nsresult rv;
 
     nsCOMPtr<nsIObserverService> observerService =
         do_GetService("@mozilla.org/observer-service;1", &rv);
     if (NS_FAILED(rv)) return;
 
+    const char *topic = node->mOffline ? "offline-load-requested" :
+                                         "prefetch-load-requested";
+
     observerService->NotifyObservers(static_cast<nsIDOMLoadStatus*>(node),
-                                     "prefetch-load-requested", nsnull);
+                                     topic, nsnull);
 }
 
 void
 nsPrefetchService::NotifyLoadCompleted(nsPrefetchNode *node)
 {
     nsresult rv;
 
     nsCOMPtr<nsIObserverService> observerService =
         do_GetService("@mozilla.org/observer-service;1", &rv);
     if (NS_FAILED(rv)) return;
 
+    const char *topic = node->mOffline ? "offline-load-completed" :
+                                         "prefetch-load-completed";
+
     observerService->NotifyObservers(static_cast<nsIDOMLoadStatus*>(node),
-                                     "prefetch-load-completed", nsnull);
+                                     topic, nsnull);
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService <private>
 //-----------------------------------------------------------------------------
 
 void
 nsPrefetchService::AddProgressListener()
@@ -546,20 +610,21 @@ nsPrefetchService::EnqueueNode(nsPrefetc
 
     return NS_OK;
 }
 
 nsresult
 nsPrefetchService::EnqueueURI(nsIURI *aURI,
                               nsIURI *aReferrerURI,
                               nsIDOMNode *aSource,
+                              PRBool aOffline,
                               nsPrefetchNode **aNode)
 {
     nsPrefetchNode *node = new nsPrefetchNode(this, aURI, aReferrerURI,
-                                              aSource);
+                                              aSource, aOffline);
     if (!node)
         return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*aNode = node);
 
     return EnqueueNode(node);
 }
 
@@ -576,33 +641,62 @@ nsPrefetchService::DequeueNode(nsPrefetc
 
     if (!mQueueHead)
         mQueueTail = nsnull;
 
     return NS_OK;
 }
 
 void
-nsPrefetchService::EmptyQueue()
+nsPrefetchService::EmptyQueue(PRBool includeOffline)
 {
     nsPrefetchNode *prev = 0;
     nsPrefetchNode *node = mQueueHead;
 
     while (node) {
         nsPrefetchNode *next = node->mNext;
-        if (prev)
-            prev->mNext = next;
+        if (includeOffline || !node->mOffline) {
+            if (prev)
+                prev->mNext = next;
+            else
+                mQueueHead = next;
+            NS_RELEASE(node);
+        }
         else
-            mQueueHead = next;
-        NS_RELEASE(node);
+            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.
     //
@@ -626,19 +720,24 @@ nsPrefetchService::StopPrefetching()
     mStopCount++;
 
     LOG(("StopPrefetching [stopcount=%d]\n", mStopCount));
 
     // only kill the prefetch queue if we've actually started prefetching.
     if (!mCurrentNode)
         return;
 
+    // if it's an offline prefetch, requeue it for when prefetching starts
+    // again
+    if (mCurrentNode->mOffline)
+        EnqueueNode(mCurrentNode);
+
     mCurrentNode->CancelChannel(NS_BINDING_ABORTED);
     mCurrentNode = nsnull;
-    EmptyQueue();
+    EmptyQueue(PR_FALSE);
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS4(nsPrefetchService,
                    nsIPrefetchService,
@@ -649,17 +748,18 @@ NS_IMPL_ISUPPORTS4(nsPrefetchService,
 //-----------------------------------------------------------------------------
 // nsPrefetchService::nsIPrefetchService
 //-----------------------------------------------------------------------------
 
 nsresult
 nsPrefetchService::Prefetch(nsIURI *aURI,
                             nsIURI *aReferrerURI,
                             nsIDOMNode *aSource,
-                            PRBool aExplicit)
+                            PRBool aExplicit,
+                            PRBool aOffline)
 {
     nsresult rv;
 
     NS_ENSURE_ARG_POINTER(aURI);
     NS_ENSURE_ARG_POINTER(aReferrerURI);
 
 #if defined(PR_LOGGING)
     if (LOG_ENABLED()) {
@@ -683,27 +783,37 @@ nsPrefetchService::Prefetch(nsIURI *aURI
     //
     // XXX we might want to either leverage nsIProtocolHandler::protocolFlags
     // or possibly nsIRequest::loadFlags to determine if this URI should be
     // prefetched.
     //
     PRBool match;
     rv = aURI->SchemeIs("http", &match); 
     if (NS_FAILED(rv) || !match) {
+        if (aOffline) {
+            // Offline https urls can be prefetched
+            rv = aURI->SchemeIs("https", &match);
+        }
+
         if (NS_FAILED(rv) || !match) {
             LOG(("rejected: URL is not of type http\n"));
             return NS_ERROR_ABORT;
         }
     }
 
     // 
     // the referrer URI must be http:
     //
     rv = aReferrerURI->SchemeIs("http", &match);
     if (NS_FAILED(rv) || !match) {
+        if (aOffline) {
+            // Offline https urls can be prefetched
+            rv = aURI->SchemeIs("https", &match);
+        }
+
         if (NS_FAILED(rv) || !match) {
             LOG(("rejected: referrer URL is not of type http\n"));
             return NS_ERROR_ABORT;
         }
     }
 
     // skip URLs that contain query strings, except URLs for which prefetching
     // has been explicitly requested.
@@ -719,35 +829,43 @@ nsPrefetchService::Prefetch(nsIURI *aURI
     }
 
     //
     // cancel if being prefetched
     //
     if (mCurrentNode) {
         PRBool equals;
         if (NS_SUCCEEDED(mCurrentNode->mURI->Equals(aURI, &equals)) && equals) {
-            LOG(("rejected: URL is already being prefetched\n"));
-            return NS_ERROR_ABORT;
+            // We may still need to put it on the queue if the channel
+            // isn't fetching to the offline cache
+            if (!aOffline || mCurrentNode->mOffline) {
+                LOG(("rejected: URL is already being prefetched\n"));
+                return NS_ERROR_ABORT;
+            }
         }
     }
 
     //
     // cancel if already on the prefetch queue
     //
     nsPrefetchNode *node = mQueueHead;
     for (; node; node = node->mNext) {
         PRBool equals;
         if (NS_SUCCEEDED(node->mURI->Equals(aURI, &equals)) && equals) {
+            if (aOffline) {
+                // make sure the node is placed in the offline cache
+                node->mOffline = PR_TRUE;
+            }
             LOG(("rejected: URL is already on prefetch queue\n"));
             return NS_ERROR_ABORT;
         }
     }
 
     nsRefPtr<nsPrefetchNode> enqueuedNode;
-    rv = EnqueueURI(aURI, aReferrerURI, aSource,
+    rv = EnqueueURI(aURI, aReferrerURI, aSource, aOffline,
                     getter_AddRefs(enqueuedNode));
     NS_ENSURE_SUCCESS(rv, rv);
 
     NotifyLoadRequested(enqueuedNode);
 
     // if there are no pages loading, kick off the request immediately
     if (mStopCount == 0 && mHaveProcessed)
         ProcessNextURI();
@@ -756,37 +874,36 @@ nsPrefetchService::Prefetch(nsIURI *aURI
 }
 
 NS_IMETHODIMP
 nsPrefetchService::PrefetchURI(nsIURI *aURI,
                                nsIURI *aReferrerURI,
                                nsIDOMNode *aSource,
                                PRBool aExplicit)
 {
-    return Prefetch(aURI, aReferrerURI, aSource, aExplicit);
+    return Prefetch(aURI, aReferrerURI, aSource, aExplicit, PR_FALSE);
 }
 
 NS_IMETHODIMP
 nsPrefetchService::PrefetchURIForOfflineUse(nsIURI *aURI,
                                             nsIURI *aReferrerURI,
                                             nsIDOMNode *aSource,
                                             PRBool aExplicit)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+    return Prefetch(aURI, aReferrerURI, aSource, aExplicit, PR_TRUE);
 }
 
 NS_IMETHODIMP
 nsPrefetchService::EnumerateQueue(PRBool aIncludeNormalItems,
                                   PRBool aIncludeOfflineItems,
                                   nsISimpleEnumerator **aEnumerator)
 {
-    NS_ENSURE_TRUE(aIncludeNormalItems && !aIncludeOfflineItems,
-                   NS_ERROR_NOT_IMPLEMENTED);
-
-    *aEnumerator = new nsPrefetchQueueEnumerator(this);
+    *aEnumerator = new nsPrefetchQueueEnumerator(this,
+                                                 aIncludeNormalItems,
+                                                 aIncludeOfflineItems);
     if (!*aEnumerator) return NS_ERROR_OUT_OF_MEMORY;
 
     NS_ADDREF(*aEnumerator);
 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
--- a/uriloader/prefetch/nsPrefetchService.h
+++ b/uriloader/prefetch/nsPrefetchService.h
@@ -51,16 +51,17 @@
 #include "nsIDOMLoadStatus.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 
 class nsPrefetchService;
 class nsPrefetchListener;
 class nsPrefetchNode;
+class nsIOfflineCacheSession;
 
 //-----------------------------------------------------------------------------
 // nsPrefetchService
 //-----------------------------------------------------------------------------
 
 class nsPrefetchService : public nsIPrefetchService
                         , public nsIWebProgressListener
                         , public nsIObserver
@@ -84,36 +85,44 @@ public:
     void NotifyLoadCompleted(nsPrefetchNode *node);
 
 private:
     ~nsPrefetchService();
 
     nsresult Prefetch(nsIURI *aURI,
                       nsIURI *aReferrerURI,
                       nsIDOMNode *aSource,
-                      PRBool aExplicit);
+                      PRBool aExplicit,
+                      PRBool aOffline);
 
     void     AddProgressListener();
     void     RemoveProgressListener();
     nsresult EnqueueURI(nsIURI *aURI, nsIURI *aReferrerURI,
-                        nsIDOMNode *aSource, nsPrefetchNode **node);
+                        nsIDOMNode *aSource, PRBool aOffline,
+                        nsPrefetchNode **node);
     nsresult EnqueueNode(nsPrefetchNode *node);
     nsresult DequeueNode(nsPrefetchNode **node);
-    void     EmptyQueue();
+    void     EmptyQueue(PRBool includeOffline);
+    nsresult SaveOfflineList(nsIURI *aDocumentUri,
+                             nsIDOMDocument *aDoc);
+    nsresult GetOfflineCacheSession(nsIOfflineCacheSession **aSession);
 
     void     StartPrefetching();
     void     StopPrefetching();
 
+    nsCOMPtr<nsIOfflineCacheSession>  mOfflineCacheSession;
     nsPrefetchNode                   *mQueueHead;
     nsPrefetchNode                   *mQueueTail;
     nsRefPtr<nsPrefetchNode>          mCurrentNode;
     PRInt32                           mStopCount;
     // true if pending document loads have ever reached zero.
     PRInt32                           mHaveProcessed;
     PRBool                            mDisabled;
+    PRBool                            mFetchedOffline;
+
 };
 
 //-----------------------------------------------------------------------------
 // nsPrefetchNode
 //-----------------------------------------------------------------------------
 
 class nsPrefetchNode : public nsIDOMLoadStatus
                      , public nsIStreamListener
@@ -126,27 +135,29 @@ public:
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIINTERFACEREQUESTOR
     NS_DECL_NSICHANNELEVENTSINK
 
     nsPrefetchNode(nsPrefetchService *aPrefetchService,
                    nsIURI *aURI,
                    nsIURI *aReferrerURI,
-                   nsIDOMNode *aSource);
+                   nsIDOMNode *aSource,
+                   PRBool aOffline);
 
     ~nsPrefetchNode() {}
 
     nsresult OpenChannel();
     nsresult CancelChannel(nsresult error);
 
     nsPrefetchNode             *mNext;
     nsCOMPtr<nsIURI>            mURI;
     nsCOMPtr<nsIURI>            mReferrerURI;
     nsCOMPtr<nsIWeakReference>  mSource;
+    PRBool                      mOffline;
 
 private:
     nsRefPtr<nsPrefetchService> mService;
     nsCOMPtr<nsIChannel>        mChannel;
     PRUint16                    mState;
     PRInt32                     mBytesRead;
 };