--- 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;
};