move the offline cache update logic from the content sink and prefetch service into an nsOfflineCacheUpdate object. bug=388839, r=biesi, sr=jst
move the offline cache update logic from the content sink and prefetch service into an nsOfflineCacheUpdate object. bug=388839, r=biesi, sr=jst
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -40,16 +40,17 @@
/*
* Base class for the XML and HTML content sinks, which construct a
* DOM based on information from the parser.
*/
#include "nsContentSink.h"
#include "nsScriptLoader.h"
#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
#include "nsICSSLoader.h"
#include "nsStyleConsts.h"
#include "nsStyleLinkElement.h"
#include "nsINodeInfo.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsCPrefetchService.h"
#include "nsIURI.h"
@@ -67,17 +68,18 @@
#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 "nsIOfflineCacheUpdate.h"
+#include "nsIDOMLoadStatus.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"
@@ -674,24 +676,22 @@ nsContentSink::ProcessLink(nsIContent* a
{
// XXX seems overkill to generate this string array
nsStringArray linkTypes;
nsStyleLinkElement::ParseLinkTypes(aRel, linkTypes);
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, aElement, hasPrefetch, PR_FALSE);
+ PrefetchHref(aHref, aElement, hasPrefetch);
}
// fetch href into the offline cache if relation is "offline-resource"
if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
- AddOfflineResource(aHref);
- if (mSaveOfflineResources)
- PrefetchHref(aHref, aElement, PR_TRUE, PR_TRUE);
+ AddOfflineResource(aHref, aElement);
}
// 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;
@@ -766,18 +766,17 @@ nsContentSink::ProcessMETATag(nsIContent
return rv;
}
void
nsContentSink::PrefetchHref(const nsAString &aHref,
nsIContent *aSource,
- PRBool aExplicit,
- PRBool aOffline)
+ PRBool aExplicit)
{
//
// SECURITY CHECK: disable prefetching from mailnews!
//
// walk up the docshell tree to see if any containing
// docshell are of type MAIL.
//
if (!mDocShell)
@@ -811,138 +810,79 @@ nsContentSink::PrefetchHref(const nsAStr
// construct URI using document charset
const nsACString &charset = mDocument->GetDocumentCharacterSet();
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), aHref,
charset.IsEmpty() ? nsnull : PromiseFlatCString(charset).get(),
mDocumentBaseURI);
if (uri) {
nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aSource);
- if (aOffline)
- prefetchService->PrefetchURIForOfflineUse(uri,
- mDocumentURI,
- domNode,
- aExplicit);
- else
- prefetchService->PrefetchURI(uri, mDocumentURI, domNode, aExplicit);
+ prefetchService->PrefetchURI(uri, mDocumentURI, domNode, 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)
+nsContentSink::AddOfflineResource(const nsAString &aHref, nsIContent *aSource)
{
PRBool match;
nsresult rv;
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(mDocumentURI);
if (!innerURI)
return NS_ERROR_FAILURE;
- nsCAutoString ownerHost;
- rv = innerURI->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 = innerURI->SchemeIs("http", &match);
NS_ENSURE_SUCCESS(rv, rv);
if (!match) {
rv = innerURI->SchemeIs("https", &match);
NS_ENSURE_SUCCESS(rv, rv);
if (!match)
return NS_OK;
}
- nsCOMPtr<nsIOfflineCacheSession> session;
- rv = GetOfflineCacheSession(getter_AddRefs(session));
+ // create updater
+ mOfflineCacheUpdate =
+ do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCAutoString ownerDomain;
+ rv = innerURI->GetHostPort(ownerDomain);
NS_ENSURE_SUCCESS(rv, rv);
- // we're going to replace the list, clear it out
- rv = session->SetOwnedKeys(ownerHost, ownerSpec, 0, nsnull);
+ nsCAutoString ownerSpec;
+ rv = mDocumentURI->GetSpec(ownerSpec);
NS_ENSURE_SUCCESS(rv, rv);
- mSaveOfflineResources = PR_TRUE;
+ rv = mOfflineCacheUpdate->Init(PR_FALSE, ownerDomain,
+ ownerSpec, mDocumentURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Kick off this update when the document is done loading
+ nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
+ mOfflineCacheUpdate->ScheduleOnDocumentStop(doc);
}
- if (!mSaveOfflineResources) return NS_OK;
+ if (!mOfflineCacheUpdate) 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<nsIDOMNode> domNode = do_QueryInterface(aSource);
- 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;
+ return mOfflineCacheUpdate->AddURI(uri, domNode);
}
void
nsContentSink::ScrollToRef()
{
if (mRef.IsEmpty()) {
return;
}
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -70,17 +70,17 @@ class nsIDocShell;
class nsICSSLoader;
class nsIParser;
class nsIAtom;
class nsIChannel;
class nsIContent;
class nsIViewManager;
class nsNodeInfoManager;
class nsScriptLoader;
-class nsIOfflineCacheSession;
+class nsIOfflineCacheUpdate;
#ifdef NS_DEBUG
extern PRLogModuleInfo* gContentSinkLogModuleInfo;
#define SINK_TRACE_CALLS 0x1
#define SINK_TRACE_REFLOW 0x2
#define SINK_ALWAYS_REFLOW 0x4
@@ -163,19 +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, nsIContent *aSource,
- PRBool aExplicit, PRBool aOffline);
- nsresult GetOfflineCacheSession(nsIOfflineCacheSession **aSession);
- nsresult AddOfflineResource(const nsAString &aHref);
+ PRBool aExplicit);
+ nsresult AddOfflineResource(const nsAString &aHref, nsIContent *aSource);
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);
@@ -257,34 +256,32 @@ protected:
// 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;
+ nsCOMPtr<nsIOfflineCacheUpdate> mOfflineCacheUpdate;
// 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;
// If true, we deferred notifications until sheets load
PRUint8 mDeferredFlushTags : 1;
// -- Can interrupt parsing members --
PRUint32 mDelayTimerStart;
// Interrupt parsing during token procesing after # of microseconds
PRInt32 mMaxTokenProcessingTime;
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -3000,26 +3000,24 @@ HTMLContentSink::ProcessLINKTag(const ns
// XXX seems overkill to generate this string array
nsStringArray linkTypes;
nsStyleLinkElement::ParseLinkTypes(relVal, linkTypes);
PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
nsAutoString hrefVal;
element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
if (!hrefVal.IsEmpty()) {
- PrefetchHref(hrefVal, element, hasPrefetch, PR_FALSE);
+ PrefetchHref(hrefVal, element, hasPrefetch);
}
}
if (linkTypes.IndexOf(NS_LITERAL_STRING("offline-resource")) != -1) {
nsAutoString hrefVal;
element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
if (!hrefVal.IsEmpty()) {
- AddOfflineResource(hrefVal);
- if (mSaveOfflineResources)
- PrefetchHref(hrefVal, element, PR_TRUE, PR_TRUE);
+ AddOfflineResource(hrefVal, element);
}
}
}
}
}
return result;
}
--- a/docshell/build/Makefile.in
+++ b/docshell/build/Makefile.in
@@ -55,16 +55,17 @@ LIBXUL_LIBRARY = 1
PACKAGE_FILE = docshell.pkg
REQUIRES = xpcom \
string \
dom \
js \
shistory \
necko \
+ nkcache \
gfx \
content \
layout \
webshell \
widget \
pref \
view \
intl \
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -49,16 +49,17 @@
#include "nsAboutRedirector.h"
// uriloader
#include "nsURILoader.h"
#include "nsDocLoader.h"
#include "nsOSHelperAppService.h"
#include "nsExternalProtocolHandler.h"
#include "nsPrefetchService.h"
+#include "nsOfflineCacheUpdate.h"
#include "nsHandlerAppImpl.h"
// session history
#include "nsSHEntry.h"
#include "nsSHistory.h"
#include "nsSHTransaction.h"
// global history
@@ -95,16 +96,19 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWe
// uriloader
NS_GENERIC_FACTORY_CONSTRUCTOR(nsURILoader)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDocLoader, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsOSHelperAppService, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsExternalProtocolHandler)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBlockedExternalProtocolHandler)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrefetchService, Init)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsOfflineCacheUpdateService,
+ nsOfflineCacheUpdateService::GetInstance)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsOfflineCacheUpdate)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsLocalHandlerApp)
#if defined(XP_MAC) || defined(XP_MACOSX)
#include "nsInternetConfigService.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsInternetConfigService)
#endif
// session history
@@ -200,16 +204,20 @@ static const nsModuleComponentInfo gDocS
{ "Netscape Mime Mapping Service", NS_EXTERNALHELPERAPPSERVICE_CID, NS_MIMESERVICE_CONTRACTID,
nsOSHelperAppServiceConstructor, },
{ "Netscape Default Protocol Handler", NS_EXTERNALPROTOCOLHANDLER_CID, NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default",
nsExternalProtocolHandlerConstructor, },
{ "Netscape Default Blocked Protocol Handler", NS_BLOCKEDEXTERNALPROTOCOLHANDLER_CID, NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default-blocked",
nsBlockedExternalProtocolHandlerConstructor, },
{ NS_PREFETCHSERVICE_CLASSNAME, NS_PREFETCHSERVICE_CID, NS_PREFETCHSERVICE_CONTRACTID,
nsPrefetchServiceConstructor, },
+ { NS_OFFLINECACHEUPDATESERVICE_CLASSNAME, NS_OFFLINECACHEUPDATESERVICE_CID, NS_OFFLINECACHEUPDATESERVICE_CONTRACTID,
+ nsOfflineCacheUpdateServiceConstructor, },
+ { NS_OFFLINECACHEUPDATE_CLASSNAME, NS_OFFLINECACHEUPDATE_CID, NS_OFFLINECACHEUPDATE_CONTRACTID,
+ nsOfflineCacheUpdateConstructor, },
{ "Local Application Handler App", NS_LOCALHANDLERAPP_CID,
NS_LOCALHANDLERAPP_CONTRACTID, nsLocalHandlerAppConstructor, },
#if defined(XP_MAC) || defined(XP_MACOSX)
{ "Internet Config Service", NS_INTERNETCONFIGSERVICE_CID, NS_INTERNETCONFIGSERVICE_CONTRACTID,
nsInternetConfigServiceConstructor, },
#endif
// session history
--- a/dom/src/base/Makefile.in
+++ b/dom/src/base/Makefile.in
@@ -61,16 +61,17 @@ REQUIRES = xpcom \
pref \
oji \
necko \
nkcache \
mimetype \
java \
locale \
uriloader \
+ prefetch \
xuldoc \
webshell \
view \
uconv \
shistory \
plugin \
windowwatcher \
chardet \
--- a/dom/src/offline/nsDOMOfflineLoadStatusList.cpp
+++ b/dom/src/offline/nsDOMOfflineLoadStatusList.cpp
@@ -149,16 +149,17 @@ nsDOMOfflineLoadStatus::GetStatus(PRUint
// nsDOMOfflineLoadStatusList
//
NS_INTERFACE_MAP_BEGIN(nsDOMOfflineLoadStatusList)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLoadStatusList)
NS_INTERFACE_MAP_ENTRY(nsIDOMLoadStatusList)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdateObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(LoadStatusList)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsDOMOfflineLoadStatusList)
NS_IMPL_RELEASE(nsDOMOfflineLoadStatusList)
nsDOMOfflineLoadStatusList::nsDOMOfflineLoadStatusList(nsIURI *aURI)
@@ -180,124 +181,41 @@ nsDOMOfflineLoadStatusList::Init()
return NS_OK;
}
mInitialized = PR_TRUE;
nsresult rv = mURI->GetHostPort(mHostPort);
NS_ENSURE_SUCCESS(rv, 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));
+ nsCOMPtr<nsIOfflineCacheUpdateService> cacheUpdateService =
+ do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
- mCacheSession = do_QueryInterface(session, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // get the current list of loads from the prefetch queue
- nsCOMPtr<nsIPrefetchService> prefetchService =
- do_GetService(NS_PREFETCHSERVICE_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsISimpleEnumerator> e;
- rv = prefetchService->EnumerateQueue(PR_FALSE, PR_TRUE, getter_AddRefs(e));
+ PRUint32 numUpdates;
+ rv = cacheUpdateService->GetNumUpdates(&numUpdates);
NS_ENSURE_SUCCESS(rv, rv);
- PRBool more;
- while (NS_SUCCEEDED(rv = e->HasMoreElements(&more)) && more) {
- nsCOMPtr<nsIDOMLoadStatus> status;
- rv = e->GetNext(getter_AddRefs(status));
- NS_ENSURE_SUCCESS(rv, rv);
-
- PRBool shouldInclude;
- rv = ShouldInclude(status, &shouldInclude);
+ for (PRUint32 i = 0; i < numUpdates; i++) {
+ nsCOMPtr<nsIOfflineCacheUpdate> cacheUpdate;
+ rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate));
NS_ENSURE_SUCCESS(rv, rv);
- if (!shouldInclude) {
- continue;
- }
+ rv = WatchUpdate(cacheUpdate);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
- nsDOMOfflineLoadStatus *wrapper = new nsDOMOfflineLoadStatus(status);
- if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
-
- mItems.AppendObject(wrapper);
- }
- NS_ENSURE_SUCCESS(rv, rv);
-
- // watch for changes in the prefetch queue
+ // watch for new offline cache updates
nsCOMPtr<nsIObserverService> observerServ =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
- rv = observerServ->AddObserver(this, "offline-load-requested", PR_TRUE);
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = observerServ->AddObserver(this, "offline-load-completed", PR_TRUE);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return NS_OK;
-}
-
-nsresult
-nsDOMOfflineLoadStatusList::ShouldInclude(nsIDOMLoadStatus *aStatus,
- PRBool *aShouldInclude)
-{
- *aShouldInclude = PR_FALSE;
-
- nsAutoString uriStr;
- nsresult rv = aStatus->GetUri(uriStr);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIURI> uri;
- rv = NS_NewURI(getter_AddRefs(uri), uriStr);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCAutoString hostport;
- rv = uri->GetHostPort(hostport);
+ rv = observerServ->AddObserver(this, "offline-cache-update-added", PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
- if (hostport != mHostPort)
- return NS_OK;
-
- // Check that the URL is owned by this domain
- nsCAutoString spec;
- rv = uri->GetSpec(spec);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIDOMNode> source;
- rv = aStatus->GetSource(getter_AddRefs(source));
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCAutoString ownerURI;
- if (source) {
- // Came from a <link> element, check that it's owned by this URI
- rv = mURI->GetSpec(ownerURI);
- NS_ENSURE_SUCCESS(rv, rv);
- } else {
- // Didn't come from a <link> element, check that it's owned by
- // resource list (no owner URI)
- ownerURI.Truncate();
- }
-
- PRBool owned;
- rv = mCacheSession->KeyIsOwned(mHostPort, ownerURI, spec, &owned);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!owned) {
- return NS_OK;
- }
-
- *aShouldInclude = PR_TRUE;
-
return NS_OK;
}
nsIDOMLoadStatus *
nsDOMOfflineLoadStatusList::FindWrapper(nsIDOMLoadStatus *aStatus,
PRUint32 *index)
{
for (int i = 0; i < mItems.Count(); i++) {
@@ -307,16 +225,55 @@ nsDOMOfflineLoadStatusList::FindWrapper(
*index = i;
return mItems[i];
}
}
return nsnull;
}
+nsresult
+nsDOMOfflineLoadStatusList::WatchUpdate(nsIOfflineCacheUpdate *aUpdate)
+{
+ nsCAutoString owner;
+ nsresult rv = aUpdate->GetUpdateDomain(owner);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (owner != mHostPort) {
+ // This update doesn't belong to us
+ return NS_OK;
+ }
+
+ PRUint32 numItems;
+ rv = aUpdate->GetCount(&numItems);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (PRUint32 i = 0; i < numItems; i++) {
+ nsCOMPtr<nsIDOMLoadStatus> status;
+ rv = aUpdate->Item(i, getter_AddRefs(status));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsDOMOfflineLoadStatus *wrapper = new nsDOMOfflineLoadStatus(status);
+ if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
+
+ mItems.AppendObject(wrapper);
+
+ rv = SendLoadEvent(NS_LITERAL_STRING(LOADREQUESTED_STR),
+ mLoadRequestedEventListeners,
+ wrapper);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aUpdate->AddObserver(this, PR_TRUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
//
// nsDOMOfflineLoadStatusList::nsIDOMLoadStatusList
//
NS_IMETHODIMP
nsDOMOfflineLoadStatusList::GetLength(PRUint32 *aLength)
{
nsresult rv = Init();
@@ -503,49 +460,42 @@ nsDOMOfflineLoadStatusList::SendLoadEven
// nsDOMLoadStatusList::nsIObserver
//
NS_IMETHODIMP
nsDOMOfflineLoadStatusList::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *aData)
{
nsresult rv;
- if (!strcmp(aTopic, "offline-load-requested")) {
- nsCOMPtr<nsIDOMLoadStatus> status = do_QueryInterface(aSubject);
- if (status) {
- PRBool shouldInclude;
- rv = ShouldInclude(status, &shouldInclude);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (!shouldInclude) return NS_OK;
-
- nsDOMOfflineLoadStatus *wrapper = new nsDOMOfflineLoadStatus(status);
- if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
-
- mItems.AppendObject(wrapper);
-
- rv = SendLoadEvent(NS_LITERAL_STRING(LOADREQUESTED_STR),
- mLoadRequestedEventListeners,
- wrapper);
+ if (!strcmp(aTopic, "offline-cache-update-added")) {
+ nsCOMPtr<nsIOfflineCacheUpdate> update = do_QueryInterface(aSubject);
+ if (update) {
+ rv = WatchUpdate(update);
NS_ENSURE_SUCCESS(rv, rv);
}
- } else if (!strcmp(aTopic, "offline-load-completed")) {
- nsCOMPtr<nsIDOMLoadStatus> status = do_QueryInterface(aSubject);
- if (status) {
- PRUint32 index;
- nsCOMPtr<nsIDOMLoadStatus> wrapper = FindWrapper(status, &index);
- if (wrapper) {
- mItems.RemoveObjectAt(index);
+ }
+
+ return NS_OK;
+}
+
+//
+// nsDOMLoadStatusList::nsIOfflineCacheUpdateObserver
+//
- rv = SendLoadEvent(NS_LITERAL_STRING(LOADCOMPLETED_STR),
- mLoadCompletedEventListeners,
- wrapper);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
+NS_IMETHODIMP
+nsDOMOfflineLoadStatusList::ItemCompleted(nsIDOMLoadStatus *aItem)
+{
+ PRUint32 index;
+ nsCOMPtr<nsIDOMLoadStatus> wrapper = FindWrapper(aItem, &index);
+ if (wrapper) {
+ mItems.RemoveObjectAt(index);
+ nsresult rv = SendLoadEvent(NS_LITERAL_STRING(LOADCOMPLETED_STR),
+ mLoadCompletedEventListeners,
+ wrapper);
+ NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
//
// nsDOMLoadStatusEvent
//
--- a/dom/src/offline/nsDOMOfflineLoadStatusList.h
+++ b/dom/src/offline/nsDOMOfflineLoadStatusList.h
@@ -38,63 +38,63 @@
#ifndef nsDOMOfflineLoadStatusList_h___
#define nsDOMOfflineLoadStatusList_h___
#include "nscore.h"
#include "nsIDOMLoadStatus.h"
#include "nsIDOMLoadStatusEvent.h"
#include "nsIDOMLoadStatusList.h"
-#include "nsIOfflineCacheSession.h"
+#include "nsIOfflineCacheUpdate.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsIURI.h"
#include "nsDOMEvent.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMEventListener.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsIScriptContext.h"
class nsDOMOfflineLoadStatus;
class nsDOMOfflineLoadStatusList : public nsIDOMLoadStatusList,
public nsIDOMEventTarget,
public nsIObserver,
+ public nsIOfflineCacheUpdateObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMLOADSTATUSLIST
NS_DECL_NSIDOMEVENTTARGET
NS_DECL_NSIOBSERVER
+ NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER
nsDOMOfflineLoadStatusList(nsIURI *aURI);
virtual ~nsDOMOfflineLoadStatusList();
nsresult Init();
private :
- nsresult ShouldInclude (nsIDOMLoadStatus *aStatus,
- PRBool *aInclude);
+ nsresult WatchUpdate (nsIOfflineCacheUpdate *aUpdate);
nsIDOMLoadStatus *FindWrapper (nsIDOMLoadStatus *aStatus,
PRUint32 *aIndex);
void NotifyEventListeners(const nsCOMArray<nsIDOMEventListener>& aListeners,
nsIDOMEvent* aEvent);
nsresult SendLoadEvent (const nsAString& aEventName,
const nsCOMArray<nsIDOMEventListener>& aListeners,
nsIDOMLoadStatus *aStatus);
PRBool mInitialized;
nsCOMPtr<nsIURI> mURI;
nsCOMArray<nsIDOMLoadStatus> mItems;
nsCString mHostPort;
- nsCOMPtr<nsIOfflineCacheSession> mCacheSession;
nsCOMPtr<nsIScriptContext> mScriptContext;
nsCOMArray<nsIDOMEventListener> mLoadRequestedEventListeners;
nsCOMArray<nsIDOMEventListener> mLoadCompletedEventListeners;
};
class nsDOMLoadStatusEvent : public nsDOMEvent,
--- a/dom/src/offline/nsDOMOfflineResourceList.cpp
+++ b/dom/src/offline/nsDOMOfflineResourceList.cpp
@@ -41,16 +41,18 @@
#include "nsDOMError.h"
#include "nsIPrefetchService.h"
#include "nsCPrefetchService.h"
#include "nsNetUtil.h"
#include "nsNetCID.h"
#include "nsICacheSession.h"
#include "nsICacheService.h"
#include "nsIOfflineCacheSession.h"
+#include "nsIOfflineCacheUpdate.h"
+#include "nsIDOMLoadStatus.h"
#include "nsAutoPtr.h"
#include "nsContentUtils.h"
// To prevent abuse of the resource list for data storage, the number
// of offline urls and their length are limited.
static const char kMaxEntriesPref[] = "offline.max_site_resources";
#define DEFAULT_MAX_ENTRIES 100
@@ -160,53 +162,40 @@ nsDOMOfflineResourceList::Add(const nsAS
if (aURI.Length() > MAX_URI_LENGTH) return NS_ERROR_DOM_BAD_URI;
// this will fail if the URI is not absolute
nsCOMPtr<nsIURI> requestedURI;
rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
NS_ENSURE_SUCCESS(rv, rv);
- // only http/https urls will work offline
- PRBool match;
- rv = requestedURI->SchemeIs("http", &match);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!match) {
- rv = requestedURI->SchemeIs("https", &match);
- NS_ENSURE_SUCCESS(rv, rv);
- if (!match) return NS_ERROR_DOM_BAD_URI;
- }
-
PRUint32 length;
rv = GetLength(&length);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 maxEntries = nsContentUtils::GetIntPref(kMaxEntriesPref,
DEFAULT_MAX_ENTRIES);
if (length > maxEntries) return NS_ERROR_NOT_AVAILABLE;
ClearCachedKeys();
- nsCAutoString key;
- rv = GetCacheKey(requestedURI, key);
+ nsCOMPtr<nsIOfflineCacheUpdate> update =
+ do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
- rv = mCacheSession->AddOwnedKey(mHostPort,
- NS_LITERAL_CSTRING(""),
- key);
+ rv = update->Init(PR_TRUE, mHostPort, NS_LITERAL_CSTRING(""), mURI);
NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIPrefetchService> prefetchService =
- do_GetService(NS_PREFETCHSERVICE_CONTRACTID, &rv);
+ rv = update->AddURI(requestedURI, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
- return prefetchService->PrefetchURIForOfflineUse(requestedURI,
- mURI,
- nsnull,
- PR_TRUE);
+ rv = update->Schedule();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
}
NS_IMETHODIMP
nsDOMOfflineResourceList::Remove(const nsAString& aURI)
{
nsresult rv = Init();
NS_ENSURE_SUCCESS(rv, rv);
@@ -249,38 +238,27 @@ nsDOMOfflineResourceList::Clear()
}
NS_IMETHODIMP
nsDOMOfflineResourceList::Refresh()
{
nsresult rv = Init();
NS_ENSURE_SUCCESS(rv, rv);
- rv = CacheKeys();
- NS_ENSURE_SUCCESS(rv, rv);
-
- // try to start fetching it now, but it's not fatal if it fails
- nsCOMPtr<nsIPrefetchService> prefetchService =
- do_GetService(NS_PREFETCHSERVICE_CONTRACTID, &rv);
+ nsCOMPtr<nsIOfflineCacheUpdate> update =
+ do_CreateInstance(NS_OFFLINECACHEUPDATE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
- for (PRUint32 i = 0; i < gCachedKeysCount; i++) {
- // this will fail if the URI is not absolute
- nsCOMPtr<nsIURI> requestedURI;
- nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), gCachedKeys[i]);
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = update->Init(PR_FALSE, mHostPort, NS_LITERAL_CSTRING(""), mURI);
+ NS_ENSURE_SUCCESS(rv, rv);
- rv = prefetchService->PrefetchURIForOfflineUse(requestedURI,
- mURI,
- nsnull,
- PR_TRUE);
- NS_ENSURE_SUCCESS(rv, rv);
- }
+ rv = update->Schedule();
+ NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
+ return rv;
}
nsresult
nsDOMOfflineResourceList::GetCacheKey(const nsAString &aURI, nsCString &aKey)
{
nsCOMPtr<nsIURI> requestedURI;
nsresult rv = NS_NewURI(getter_AddRefs(requestedURI), aURI);
NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/tests/mochitest/ajax/offline/test_pendingOfflineLoads.html
+++ b/dom/tests/mochitest/ajax/offline/test_pendingOfflineLoads.html
@@ -99,20 +99,16 @@ function run_test()
{
navigator.pendingOfflineLoads.addEventListener("loadrequested",
load_requested,
false);
navigator.pendingOfflineLoads.addEventListener("loadcompleted",
load_completed,
false);
- // The <link rel="offline-resource"> should already be in the queue
- ok(navigator.pendingOfflineLoads.length == 2,
- "<link rel=\"offline-resource\"> loads should already be in the queue");
-
for (var i = 0; i < navigator.pendingOfflineLoads.length; i++) {
var load = navigator.pendingOfflineLoads[i];
check_load_added(navigator.pendingOfflineLoads[i]);
}
// Add the correct resources
navigator.offlineResources.add("http://localhost:8888/unknown2");
navigator.offlineResources.add(thisURL);
--- a/netwerk/cache/public/nsIOfflineCacheSession.idl
+++ b/netwerk/cache/public/nsIOfflineCacheSession.idl
@@ -35,17 +35,17 @@
* 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)]
+[scriptable, uuid(0058c32b-0d93-4cf8-a561-e6f749c8a7b1)]
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.
@@ -62,16 +62,36 @@ interface nsIOfflineCacheSession : nsISu
* 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.
*/
/**
+ * Gets the list of owner domains in the cache.
+ *
+ * @param count The number of domains returned
+ * @param uris The domains that own resources in the cache
+ */
+ void getOwnerDomains(out unsigned long count,
+ [array, size_is(count)]out string domains);
+
+ /**
+ * Gets the list of owner URIs associated with a domain.
+ *
+ * @param ownerDomain The domain to query
+ * @param count The number of uris returned
+ * @param uris The uris in this domain that own resources
+ */
+ void getOwnerURIs(in ACString ownerDomain,
+ out unsigned long count,
+ [array, size_is(count)]out string uris);
+
+ /**
* 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.
--- a/netwerk/cache/src/nsCacheService.cpp
+++ b/netwerk/cache/src/nsCacheService.cpp
@@ -825,16 +825,59 @@ nsCacheService::IsStorageEnabledForPolic
if (gService->mEnableOfflineDevice &&
storagePolicy == nsICache::STORE_OFFLINE) {
return PR_TRUE;
}
return PR_FALSE;
}
+
+nsresult nsCacheService::GetOfflineOwnerDomains(nsCacheSession * session,
+ PRUint32 * count,
+ char *** domains)
+{
+#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->GetOwnerDomains(session->ClientID()->get(),
+ count, domains);
+#else // !NECKO_OFFLINE_CACHE
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+nsresult nsCacheService::GetOfflineOwnerURIs(nsCacheSession * session,
+ const nsACString & ownerDomain,
+ PRUint32 * count,
+ char *** uris)
+{
+#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->GetOwnerURIs(session->ClientID()->get(),
+ ownerDomain, count, uris);
+#else // !NECKO_OFFLINE_CACHE
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
nsresult
nsCacheService::SetOfflineOwnedKeys(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerURI,
PRUint32 count,
const char ** keys)
{
#ifdef NECKO_OFFLINE_CACHE
--- a/netwerk/cache/src/nsCacheService.h
+++ b/netwerk/cache/src/nsCacheService.h
@@ -93,16 +93,24 @@ public:
nsICacheEntryDescriptor ** result);
static nsresult EvictEntriesForSession(nsCacheSession * session);
static nsresult IsStorageEnabledForPolicy(nsCacheStoragePolicy storagePolicy,
PRBool * result);
+ static nsresult GetOfflineOwnerDomains(nsCacheSession * session,
+ PRUint32 * count,
+ char *** domains);
+ static nsresult GetOfflineOwnerURIs(nsCacheSession * session,
+ const nsACString & ownerDomain,
+ PRUint32 * count,
+ char *** uris);
+
static nsresult SetOfflineOwnedKeys(nsCacheSession * session,
const nsACString & ownerDomain,
const nsACString & ownerUri,
PRUint32 count,
const char ** keys);
static nsresult GetOfflineOwnedKeys(nsCacheSession * session,
const nsACString & ownerDomain,
--- a/netwerk/cache/src/nsCacheSession.cpp
+++ b/netwerk/cache/src/nsCacheSession.cpp
@@ -131,16 +131,29 @@ NS_IMETHODIMP nsCacheSession::EvictEntri
NS_IMETHODIMP nsCacheSession::IsStorageEnabled(PRBool *result)
{
return nsCacheService::IsStorageEnabledForPolicy(StoragePolicy(), result);
}
+NS_IMETHODIMP nsCacheSession::GetOwnerDomains(PRUint32 * count,
+ char *** domains)
+{
+ return nsCacheService::GetOfflineOwnerDomains(this, count, domains);
+}
+
+NS_IMETHODIMP nsCacheSession::GetOwnerURIs(const nsACString & domain,
+ PRUint32 * count,
+ char *** uris)
+{
+ return nsCacheService::GetOfflineOwnerURIs(this, domain, count, uris);
+}
+
NS_IMETHODIMP nsCacheSession::SetOwnedKeys(const nsACString & domain,
const nsACString & uri,
PRUint32 count,
const char ** keys)
{
return nsCacheService::SetOfflineOwnedKeys(this, domain, uri, count, keys);
}
--- a/netwerk/cache/src/nsDiskCacheDeviceSQL.cpp
+++ b/netwerk/cache/src/nsDiskCacheDeviceSQL.cpp
@@ -784,17 +784,19 @@ nsOfflineCacheDevice::Init()
StatementSql ( mStatement_FindEntry, "SELECT MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
StatementSql ( mStatement_BindEntry, "INSERT INTO moz_cache (ClientID, Key, MetaData, Generation, Flags, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime) VALUES(?,?,?,?,?,?,?,?,?,?);" ),
StatementSql ( mStatement_ClearOwnership, "DELETE FROM moz_cache_owners WHERE ClientId = ? AND Domain = ? AND URI = ?;" ),
StatementSql ( mStatement_RemoveOwnership, "DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?;" ),
StatementSql ( mStatement_ClearDomain, "DELETE FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?;" ),
StatementSql ( mStatement_AddOwnership, "INSERT INTO moz_cache_owners (ClientID, Domain, URI, Key) VALUES (?, ?, ?, ?);" ),
StatementSql ( mStatement_CheckOwnership, "SELECT Key From moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ? AND Key = ?;" ),
StatementSql ( mStatement_ListOwned, "SELECT Key FROM moz_cache_owners WHERE ClientID = ? AND Domain = ? AND URI = ?;" ),
- StatementSql ( 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);" ),
+ StatementSql ( mStatement_ListOwnerDomains, "SELECT DISTINCT Domain FROM moz_cache_owners WHERE ClientID = ?;"),
+ StatementSql ( mStatement_ListOwnerURIs, "SELECT DISTINCT URI FROM moz_cache_owners WHERE ClientID = ? AND Domain = ?;"),
+ StatementSql ( 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);
@@ -1247,16 +1249,87 @@ nsOfflineCacheDevice::EvictEntries(const
if (clientID)
PR_smprintf_free((char *) deleteCmd);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
+nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement,
+ PRUint32 resultIndex,
+ PRUint32 * count,
+ char *** values)
+{
+ PRBool hasRows;
+ nsresult rv = statement->ExecuteStep(&hasRows);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsTArray<nsCString> valArray;
+ while (hasRows)
+ {
+ PRUint32 length;
+ valArray.AppendElement(
+ nsDependentCString(statement->AsSharedUTF8String(resultIndex, &length)));
+
+ rv = statement->ExecuteStep(&hasRows);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *count = valArray.Length();
+ char **ret = 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(valArray[i].get());
+ if (!ret[i]) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ *values = ret;
+
+ return NS_OK;
+}
+
+nsresult
+nsOfflineCacheDevice::GetOwnerDomains(const char * clientID,
+ PRUint32 * count,
+ char *** domains)
+{
+ LOG(("nsOfflineCacheDevice::GetOwnerDomains [cid=%s]\n", clientID));
+
+ AutoResetStatement statement(mStatement_ListOwnerDomains);
+ nsresult rv = statement->BindUTF8StringParameter(
+ 0, nsDependentCString(clientID));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return RunSimpleQuery(mStatement_ListOwnerDomains, 0, count, domains);
+}
+
+nsresult
+nsOfflineCacheDevice::GetOwnerURIs(const char * clientID,
+ const nsACString & ownerDomain,
+ PRUint32 * count,
+ char *** domains)
+{
+ LOG(("nsOfflineCacheDevice::GetOwnerURIs [cid=%s]\n", clientID));
+
+ AutoResetStatement statement(mStatement_ListOwnerURIs);
+ nsresult rv = statement->BindUTF8StringParameter(
+ 0, nsDependentCString(clientID));
+ rv = statement->BindUTF8StringParameter(
+ 1, ownerDomain);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return RunSimpleQuery(mStatement_ListOwnerURIs, 0, count, domains);
+}
+
+nsresult
nsOfflineCacheDevice::SetOwnedKeys(const char * clientID,
const nsACString & ownerDomain,
const nsACString & ownerURI,
PRUint32 count,
const char ** keys)
{
LOG(("nsOfflineCacheDevice::SetOwnedKeys [cid=%s]\n", clientID));
mozStorageTransaction transaction(mDB, PR_FALSE);
@@ -1300,46 +1373,17 @@ nsOfflineCacheDevice::GetOwnedKeys(const
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 = 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;
+ return RunSimpleQuery(mStatement_ListOwned, 0, count, keys);
}
nsresult
nsOfflineCacheDevice::AddOwnedKey(const char * clientID,
const nsACString & ownerDomain,
const nsACString & ownerURI,
const nsACString & key)
{
--- a/netwerk/cache/src/nsDiskCacheDeviceSQL.h
+++ b/netwerk/cache/src/nsDiskCacheDeviceSQL.h
@@ -81,16 +81,23 @@ public:
virtual nsresult OnDataSizeChange(nsCacheEntry * entry, PRInt32 deltaSize);
virtual nsresult Visit(nsICacheVisitor * visitor);
virtual nsresult EvictEntries(const char * clientID);
/* Entry ownership */
+ nsresult GetOwnerDomains(const char * clientID,
+ PRUint32 * count,
+ char *** domains);
+ nsresult GetOwnerURIs(const char * clientID,
+ const nsACString & ownerDomain,
+ PRUint32 * count,
+ char *** uris);
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,
@@ -130,16 +137,20 @@ public:
private:
PRBool Initialized() { return mDB != nsnull; }
nsresult UpdateEntry(nsCacheEntry *entry);
nsresult UpdateEntrySize(nsCacheEntry *entry, PRUint32 newSize);
nsresult DeleteEntry(nsCacheEntry *entry, PRBool deleteData);
nsresult DeleteData(nsCacheEntry *entry);
nsresult EnableEvictionObserver();
nsresult DisableEvictionObserver();
+ nsresult RunSimpleQuery(mozIStorageStatement *statment,
+ PRUint32 resultIndex,
+ PRUint32 * count,
+ char *** values);
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;
@@ -147,15 +158,17 @@ private:
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<mozIStorageStatement> mStatement_ListOwnerDomains;
+ nsCOMPtr<mozIStorageStatement> mStatement_ListOwnerURIs;
nsCOMPtr<nsILocalFile> mCacheDirectory;
PRUint32 mCacheCapacity;
PRInt32 mDeltaCounter;
};
#endif // nsOfflineCacheDevice_h__
--- a/uriloader/prefetch/Makefile.in
+++ b/uriloader/prefetch/Makefile.in
@@ -52,19 +52,22 @@ REQUIRES = xpcom \
uriloader \
nkcache \
chardet \
pref \
$(NULL)
CPPSRCS = \
nsPrefetchService.cpp \
+ nsOfflineCacheUpdate.cpp \
$(NULL)
+
XPIDLSRCS = \
nsIPrefetchService.idl \
+ nsIOfflineCacheUpdate.idl \
$(NULL)
EXPORTS = \
nsCPrefetchService.h \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a static lib.
FORCE_STATIC_LIB = 1
--- a/uriloader/prefetch/nsCPrefetchService.h
+++ b/uriloader/prefetch/nsCPrefetchService.h
@@ -50,9 +50,42 @@
#define NS_PREFETCHSERVICE_CID \
{ /* 6b8bdffc-3394-417d-be83-a81b7c0f63bf */ \
0x6b8bdffc, \
0x3394, \
0x417d, \
{0xbe, 0x83, 0xa8, 0x1b, 0x7c, 0x0f, 0x63, 0xbf} \
}
+/**
+ * nsOfflineCacheUpdateService : nsIOfflineCacheUpdateService
+ */
+
+#define NS_OFFLINECACHEUPDATESERVICE_CLASSNAME \
+ "nsOfflineCacheUpdateService"
+#define NS_OFFLINECACHEUPDATESERVICE_CONTRACTID \
+ "@mozilla.org/offlinecacheupdate-service;1"
+#define NS_OFFLINECACHEUPDATESERVICE_CID \
+{ /* ec06f3fc-70db-4ecd-94e0-a6e91ca44d8a */ \
+ 0xec06f3fc, \
+ 0x70db, \
+ 0x4ecd , \
+ {0x94, 0xe0, 0xa6, 0xe9, 0x1c, 0xa4, 0x4d, 0x8a} \
+}
+
+/**
+ * nsOfflineCacheUpdate : nsIOfflineCacheUpdate
+ */
+
+#define NS_OFFLINECACHEUPDATE_CLASSNAME \
+ "nsOfflineCacheUpdate"
+#define NS_OFFLINECACHEUPDATE_CONTRACTID \
+ "@mozilla.org/offlinecacheupdate;1"
+#define NS_OFFLINECACHEUPDATE_CID \
+{ /* e56f5e01-c7cc-4675-a9d7-b8f1e4127295 */ \
+ 0xe56f5e01, \
+ 0xc7cc, \
+ 0x4675, \
+ {0xa9, 0xd7, 0xb8, 0xf1, 0xe4, 0x12, 0x72, 0x95} \
+}
+
+
#endif // !nsCPrefetchService_h__
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/nsIOfflineCacheUpdate.idl
@@ -0,0 +1,159 @@
+/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * 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"
+
+interface nsIURI;
+interface nsIDOMNode;
+interface nsIDOMDocument;
+interface nsIDOMLoadStatus;
+
+[scriptable, uuid(e0785ebb-b3a1-426a-a70e-be2b923e973e)]
+interface nsIOfflineCacheUpdateObserver : nsISupports {
+ /**
+ * An item has finished loading.
+ *
+ * @param aItem load status for the item that completed.
+ */
+ void itemCompleted(in nsIDOMLoadStatus aItem);
+};
+
+/**
+ * An nsIOfflineCacheUpdate is used to update a domain's offline resources.
+ * It can be used to perform partial or complete updates.
+ *
+ * Each update object maintains a list of nsIDOMLoadStatus items for the
+ * resources it is updating. The list of these items will be available
+ * after the object is scheduled.
+ *
+ * One update object will be updating at a time. The active object will
+ * load its items one by one, sending itemCompleted() to any registered
+ * observers.
+ */
+[scriptable, uuid(7dc06ede-1098-4384-b95e-65525ab254c9)]
+interface nsIOfflineCacheUpdate : nsISupports {
+ /**
+ * The domain being updated, and the domain that will own any URIs added
+ * with this update.
+ */
+ readonly attribute ACString updateDomain;
+
+ /**
+ * The URI that will own any URIs added by this update
+ */
+ readonly attribute ACString ownerURI;
+
+ /**
+ * Initialize the update.
+ *
+ * @param aPartialUpdate
+ * TRUE if the update should just update the URIs given to it,
+ * FALSE if all URLs for the owner domain should be added.
+ * @param aUpdateDomain
+ * The domain which is being updated, and which will own any
+ * URIs added.
+ * @param aOwnerURI
+ * The owner URI for any URIs added.
+ * @param aReferrerURI
+ * The page that is requesting the update.
+ */
+ void init(in boolean aPartialUpdate,
+ in ACString aUpdateDomain,
+ in ACString aOwnerURI,
+ in nsIURI aReferrerURI);
+
+ /**
+ * Add a URI to the offline cache as part of the update.
+ *
+ * @param aURI
+ * The URI to add.
+ * @param aSource
+ * The DOM node (<link> tag) associated with this node (for
+ * implementing nsIDOMLoadStatus).
+ */
+ void addURI(in nsIURI aURI, in nsIDOMNode aSource);
+
+ /**
+ * Add the update to the offline update queue. An offline-cache-update-added
+ * event will be sent to the observer service.
+ */
+ void schedule();
+
+ /**
+ * Request that the update be scheduled when a document finishes loading.
+ *
+ * @param aDocument
+ * When this document finishes loading, the update will be scheduled.
+ */
+ void scheduleOnDocumentStop(in nsIDOMDocument aDocument);
+
+ /**
+ * Access to the list of items in the update.
+ */
+ readonly attribute unsigned long count;
+ nsIDOMLoadStatus item(in unsigned long index);
+
+ /**
+ * Observe loads that are added to the update.
+ *
+ * @param aObserver
+ * object that notifications will be sent to.
+ * @param aHoldWeak
+ * TRUE if you want the update to hold a weak reference to the
+ * observer, FALSE for a strong reference.
+ */
+ void addObserver(in nsIOfflineCacheUpdateObserver aObserver,
+ in boolean aHoldWeak);
+
+ /**
+ * Remove an observer from the update.
+ *
+ * @param aObserver
+ * the observer to remove.
+ */
+ void removeObserver(in nsIOfflineCacheUpdateObserver aObserver);
+};
+
+[scriptable, uuid(f99ca10f-5cde-4966-b845-433f2921a201)]
+interface nsIOfflineCacheUpdateService : nsISupports {
+ /**
+ * Access to the list of cache updates that have been scheduled.
+ */
+ readonly attribute unsigned long numUpdates;
+ nsIOfflineCacheUpdate getUpdate(in unsigned long index);
+};
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp
@@ -0,0 +1,1058 @@
+/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * 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 "nsOfflineCacheUpdate.h"
+
+#include "nsCPrefetchService.h"
+#include "nsCURILoader.h"
+#include "nsICache.h"
+#include "nsICacheService.h"
+#include "nsICacheSession.h"
+#include "nsICachingChannel.h"
+#include "nsIDOMWindow.h"
+#include "nsIObserverService.h"
+#include "nsIOfflineCacheSession.h"
+#include "nsIWebProgress.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStreamUtils.h"
+#include "prlog.h"
+
+static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull;
+
+#if defined(PR_LOGGING)
+//
+// To enable logging (see prlog.h for full details):
+//
+// set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
+// set NSPR_LOG_FILE=offlineupdate.log
+//
+// this enables PR_LOG_ALWAYS level information and places all output in
+// the file offlineupdate.log
+//
+static PRLogModuleInfo *gOfflineCacheUpdateLog;
+#endif
+#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args)
+#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4)
+
+class AutoFreeArray {
+public:
+ AutoFreeArray(PRUint32 count, char **values)
+ : mCount(count), mValues(values) {};
+ ~AutoFreeArray() { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mValues); }
+private:
+ PRUint32 mCount;
+ char **mValues;
+};
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateItem::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS5(nsOfflineCacheUpdateItem,
+ nsIDOMLoadStatus,
+ nsIRequestObserver,
+ nsIStreamListener,
+ nsIInterfaceRequestor,
+ nsIChannelEventSink)
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateItem <public>
+//-----------------------------------------------------------------------------
+
+nsOfflineCacheUpdateItem::nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
+ nsIURI *aURI,
+ nsIURI *aReferrerURI,
+ nsIDOMNode *aSource)
+ : mURI(aURI)
+ , mReferrerURI(aReferrerURI)
+ , mUpdate(aUpdate)
+ , mChannel(nsnull)
+ , mState(nsIDOMLoadStatus::UNINITIALIZED)
+ , mBytesRead(0)
+{
+ mSource = do_GetWeakReference(aSource);
+}
+
+nsOfflineCacheUpdateItem::~nsOfflineCacheUpdateItem()
+{
+}
+
+nsresult
+nsOfflineCacheUpdateItem::OpenChannel()
+{
+ nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
+ mURI,
+ nsnull, nsnull, this,
+ nsIRequest::LOAD_BACKGROUND |
+ nsICachingChannel::LOAD_ONLY_IF_MODIFIED);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // configure HTTP specific stuff
+ nsCOMPtr<nsIHttpChannel> httpChannel =
+ do_QueryInterface(mChannel);
+ if (httpChannel) {
+ httpChannel->SetReferrer(mReferrerURI);
+ httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-Moz"),
+ NS_LITERAL_CSTRING("offline-resource"),
+ PR_FALSE);
+ }
+
+ 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;
+}
+
+nsresult
+nsOfflineCacheUpdateItem::Cancel()
+{
+ if (mChannel) {
+ mChannel->Cancel(NS_ERROR_ABORT);
+ mChannel = nsnull;
+ }
+
+ mState = nsIDOMLoadStatus::UNINITIALIZED;
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateItem::nsIStreamListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::OnStartRequest(nsIRequest *aRequest,
+ nsISupports *aContext)
+{
+ mState = nsIDOMLoadStatus::RECEIVING;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::OnDataAvailable(nsIRequest *aRequest,
+ nsISupports *aContext,
+ nsIInputStream *aStream,
+ PRUint32 aOffset,
+ PRUint32 aCount)
+{
+ PRUint32 bytesRead = 0;
+ aStream->ReadSegments(NS_DiscardSegment, nsnull, aCount, &bytesRead);
+ mBytesRead += bytesRead;
+ LOG(("loaded %u bytes into offline cache [offset=%u]\n",
+ bytesRead, aOffset));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::OnStopRequest(nsIRequest *aRequest,
+ nsISupports *aContext,
+ nsresult aStatus)
+{
+ LOG(("done fetching offline item [status=%x]\n", aStatus));
+
+ mState = nsIDOMLoadStatus::LOADED;
+
+ if (mBytesRead == 0 && aStatus == NS_OK) {
+ // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
+ // specified), but the object should report loadedSize as if it
+ // did.
+ mChannel->GetContentLength(&mBytesRead);
+ }
+
+ mUpdate->LoadCompleted();
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateItem::nsIInterfaceRequestor
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::GetInterface(const nsIID &aIID, void **aResult)
+{
+ if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
+ NS_ADDREF_THIS();
+ *aResult = static_cast<nsIChannelEventSink *>(this);
+ return NS_OK;
+ }
+
+ return NS_ERROR_NO_INTERFACE;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateItem::nsIChannelEventSink
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::OnChannelRedirect(nsIChannel *aOldChannel,
+ nsIChannel *aNewChannel,
+ PRUint32 aFlags)
+{
+ nsCOMPtr<nsIURI> newURI;
+ nsresult rv = aNewChannel->GetURI(getter_AddRefs(newURI));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsICachingChannel> oldCachingChannel =
+ do_QueryInterface(aOldChannel);
+ nsCOMPtr<nsICachingChannel> newCachingChannel =
+ do_QueryInterface(aOldChannel);
+ if (newCachingChannel)
+ newCachingChannel->SetCacheForOfflineUse(PR_TRUE);
+
+ PRBool match;
+ rv = newURI->SchemeIs("http", &match);
+ if (NS_FAILED(rv) || !match) {
+ if (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("offline-resource"),
+ PR_FALSE);
+
+ mChannel = aNewChannel;
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateItem::nsIDOMLoadStatus
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::GetSource(nsIDOMNode **aSource)
+{
+ if (mSource) {
+ return CallQueryReferent(mSource.get(), aSource);
+ } else {
+ *aSource = nsnull;
+ return NS_OK;
+ }
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::GetUri(nsAString &aURI)
+{
+ nsCAutoString spec;
+ nsresult rv = mURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CopyUTF8toUTF16(spec, aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::GetTotalSize(PRInt32 *aTotalSize)
+{
+ if (mChannel) {
+ return mChannel->GetContentLength(aTotalSize);
+ }
+
+ *aTotalSize = -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::GetLoadedSize(PRInt32 *aLoadedSize)
+{
+ *aLoadedSize = mBytesRead;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::GetReadyState(PRUint16 *aReadyState)
+{
+ *aReadyState = mState;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateItem::GetStatus(PRUint16 *aStatus)
+{
+ if (!mChannel) {
+ *aStatus = 0;
+ return NS_OK;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PRUint32 httpStatus;
+ rv = httpChannel->GetResponseStatus(&httpStatus);
+ if (rv == NS_ERROR_NOT_AVAILABLE) {
+ // Someone's calling this before we got a response... Check our
+ // ReadyState. If we're at RECEIVING or LOADED, then this means the
+ // connection errored before we got any data; return a somewhat
+ // sensible error code in that case.
+ if (mState >= nsIDOMLoadStatus::RECEIVING) {
+ *aStatus = NS_ERROR_NOT_AVAILABLE;
+ return NS_OK;
+ }
+
+ *aStatus = 0;
+ return NS_OK;
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aStatus = PRUint16(httpStatus);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdate::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS1(nsOfflineCacheUpdate,
+ nsIOfflineCacheUpdate);
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdate <public>
+//-----------------------------------------------------------------------------
+
+nsOfflineCacheUpdate::nsOfflineCacheUpdate()
+ : mState(STATE_UNINITIALIZED)
+ , mAddedItems(PR_FALSE)
+ , mPartialUpdate(PR_FALSE)
+{
+}
+
+nsOfflineCacheUpdate::~nsOfflineCacheUpdate()
+{
+ LOG(("nsOfflineCacheUpdate::~nsOfflineCacheUpdate [%p]", this));
+}
+
+nsresult
+nsOfflineCacheUpdate::Init(PRBool aPartialUpdate,
+ const nsACString &aUpdateDomain,
+ const nsACString &aOwnerURI,
+ nsIURI *aReferrerURI)
+{
+ nsresult rv;
+
+ // Make sure the service has been initialized
+ if (!nsOfflineCacheUpdateService::GetInstance()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ LOG(("nsOfflineCacheUpdate::Init [%p]", this));
+
+ mPartialUpdate = aPartialUpdate;
+ mUpdateDomain = aUpdateDomain;
+ mOwnerURI = aOwnerURI;
+ mReferrerURI = aReferrerURI;
+
+ nsCOMPtr<nsICacheService> cacheService =
+ do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsICacheSession> session;
+ rv = cacheService->CreateSession("HTTP-offline",
+ nsICache::STORE_OFFLINE,
+ nsICache::STREAM_BASED,
+ getter_AddRefs(session));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mCacheSession = do_QueryInterface(session, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mState = STATE_INITIALIZED;
+
+ return NS_OK;
+}
+
+void
+nsOfflineCacheUpdate::LoadCompleted()
+{
+ nsresult rv;
+
+ LOG(("nsOfflineCacheUpdate::LoadCompleted [%p]", this));
+
+ NS_ASSERTION(mItems.Length() >= 1, "Unknown load completed");
+
+ nsRefPtr<nsOfflineCacheUpdateItem> item = mItems[0];
+ mItems.RemoveElementAt(0);
+
+ rv = NotifyCompleted(item);
+ if (NS_FAILED(rv)) return;
+
+ ProcessNextURI();
+}
+
+nsresult
+nsOfflineCacheUpdate::Begin()
+{
+ LOG(("nsOfflineCacheUpdate::Begin [%p]", this));
+
+ if (!mPartialUpdate) {
+ // All offline items for a domain should be updated as a group; add
+ // the other offline items requested for this domain
+ nsresult rv = AddDomainItems();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ mState = STATE_RUNNING;
+
+ ProcessNextURI();
+
+ return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdate::Cancel()
+{
+ LOG(("nsOfflineCacheUpdate::Cancel [%p]", this));
+
+ mState = STATE_CANCELLED;
+
+ if (mItems.Length() > 0) {
+ // First load might be running
+ mItems[0]->Cancel();
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdate <private>
+//-----------------------------------------------------------------------------
+
+nsresult
+nsOfflineCacheUpdate::AddOwnedItems(const nsACString &aOwnerURI)
+{
+ PRUint32 count;
+ char **keys;
+ nsresult rv = mCacheSession->GetOwnedKeys(mUpdateDomain, aOwnerURI,
+ &count, &keys);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ AutoFreeArray autoFree(count, keys);
+
+ for (PRUint32 i = 0; i < count; i++) {
+ nsCOMPtr<nsIURI> uri;
+ if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), keys[i]))) {
+ nsRefPtr<nsOfflineCacheUpdateItem> item =
+ new nsOfflineCacheUpdateItem(this, uri, mReferrerURI, nsnull);
+ if (!item) return NS_ERROR_OUT_OF_MEMORY;
+
+ mItems.AppendElement(item);
+ }
+ }
+
+ return NS_OK;
+}
+
+// Add all URIs needed by this domain to the update
+nsresult
+nsOfflineCacheUpdate::AddDomainItems()
+{
+ PRUint32 count;
+ char **uris;
+ nsresult rv = mCacheSession->GetOwnerURIs(mUpdateDomain, &count, &uris);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ AutoFreeArray autoFree(count, uris);
+
+ for (PRUint32 i = 0; i < count; i++) {
+ const char *ownerURI = uris[i];
+ // if this update includes changes to this owner URI, ignore the
+ // set in the database.
+ if (!mAddedItems || !mOwnerURI.Equals(ownerURI)) {
+ rv = AddOwnedItems(nsDependentCString(ownerURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdate::ProcessNextURI()
+{
+ LOG(("nsOfflineCacheUpdate::ProcessNextURI [%p, numItems=%d]",
+ this, mItems.Length()));
+
+ if (mState == STATE_CANCELLED || mItems.Length() == 0) {
+ return Finish();
+ }
+
+#if defined(PR_LOGGING)
+ if (LOG_ENABLED()) {
+ nsCAutoString spec;
+ mItems[0]->mURI->GetSpec(spec);
+ LOG(("%p: Opening channel for %s", this, spec.get()));
+ }
+#endif
+
+ nsresult rv = mItems[0]->OpenChannel();
+ if (NS_FAILED(rv)) {
+ LoadCompleted();
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdate::NotifyCompleted(nsOfflineCacheUpdateItem *aItem)
+{
+ nsCOMArray<nsIOfflineCacheUpdateObserver> observers;
+
+ for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
+ nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
+ do_QueryReferent(mWeakObservers[i]);
+ if (observer)
+ observers.AppendObject(observer);
+ else
+ mWeakObservers.RemoveObjectAt(i--);
+ }
+
+ for (PRInt32 i = 0; i < mObservers.Count(); i++) {
+ observers.AppendObject(mObservers[i]);
+ }
+
+ for (PRInt32 i = 0; i < observers.Count(); i++) {
+ observers[i]->ItemCompleted(aItem);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdate::Finish()
+{
+ LOG(("nsOfflineCacheUpdate::Finish [%p]", this));
+
+ mState = STATE_FINISHED;
+
+ nsOfflineCacheUpdateService *service =
+ nsOfflineCacheUpdateService::GetInstance();
+
+ if (!service)
+ return NS_ERROR_FAILURE;
+
+ return service->UpdateFinished(this);
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdate::nsIOfflineCacheUpdate
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::GetUpdateDomain(nsACString &aUpdateDomain)
+{
+ NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
+
+ aUpdateDomain = mUpdateDomain;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::GetOwnerURI(nsACString &aOwnerURI)
+{
+ NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
+
+ aOwnerURI = mOwnerURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::AddURI(nsIURI *aURI, nsIDOMNode *aSource)
+{
+ NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
+
+ if (mState >= STATE_RUNNING)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ // only http and https urls can be put in the offline cache
+ PRBool match;
+ nsresult rv = aURI->SchemeIs("http", &match);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!match) {
+ rv = aURI->SchemeIs("https", &match);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!match)
+ return NS_ERROR_ABORT;
+ }
+
+ // Save the cache key as an owned URI
+ nsCAutoString spec;
+ rv = aURI->GetSpec(spec);
+ 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);
+ rv = mCacheSession->AddOwnedKey(mUpdateDomain, mOwnerURI,
+ Substring(specEnd, specStart));
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ rv = mCacheSession->AddOwnedKey(mUpdateDomain, mOwnerURI, spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsRefPtr<nsOfflineCacheUpdateItem> item =
+ new nsOfflineCacheUpdateItem(this, aURI, mReferrerURI, aSource);
+ if (!item) return NS_ERROR_OUT_OF_MEMORY;
+
+ mItems.AppendElement(item);
+ mAddedItems = PR_TRUE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::GetCount(PRUint32 *aNumItems)
+{
+ LOG(("nsOfflineCacheUpdate::GetNumItems [%p, num=%d]",
+ this, mItems.Length()));
+
+ NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
+
+ *aNumItems = mItems.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::Item(PRUint32 aIndex, nsIDOMLoadStatus **aItem)
+{
+ LOG(("nsOfflineCacheUpdate::GetItems [%p, index=%d]", this, aIndex));
+
+ NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
+
+ if (aIndex < mItems.Length())
+ NS_IF_ADDREF(*aItem = mItems.ElementAt(aIndex));
+ else
+ *aItem = nsnull;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver,
+ PRBool aHoldWeak)
+{
+ LOG(("nsOfflineCacheUpdate::AddObserver [%p]", this));
+
+ NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
+
+ if (aHoldWeak) {
+ nsCOMPtr<nsIWeakReference> weakRef = do_GetWeakReference(aObserver);
+ mWeakObservers.AppendObject(weakRef);
+ } else {
+ mObservers.AppendObject(aObserver);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver)
+{
+ LOG(("nsOfflineCacheUpdate::RemoveObserver [%p]", this));
+
+ NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED);
+
+ for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) {
+ nsCOMPtr<nsIOfflineCacheUpdateObserver> observer =
+ do_QueryReferent(mWeakObservers[i]);
+ if (observer == aObserver) {
+ mWeakObservers.RemoveObjectAt(i);
+ return NS_OK;
+ }
+ }
+
+ for (PRInt32 i = 0; i < mObservers.Count(); i++) {
+ if (mObservers[i] == aObserver) {
+ mObservers.RemoveObjectAt(i);
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::Schedule()
+{
+ LOG(("nsOfflineCacheUpdate::Schedule [%p]", this));
+
+ nsOfflineCacheUpdateService *service =
+ nsOfflineCacheUpdateService::GetInstance();
+
+ if (!service) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return service->Schedule(this);
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdate::ScheduleOnDocumentStop(nsIDOMDocument *aDocument)
+{
+ LOG(("nsOfflineCacheUpdate::ScheduleOnDocumentStop [%p]", this));
+
+ nsOfflineCacheUpdateService *service =
+ nsOfflineCacheUpdateService::GetInstance();
+
+ if (!service) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return service->ScheduleOnDocumentStop(this, aDocument);
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS4(nsOfflineCacheUpdateService,
+ nsIOfflineCacheUpdateService,
+ nsIWebProgressListener,
+ nsIObserver,
+ nsISupportsWeakReference)
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService <public>
+//-----------------------------------------------------------------------------
+
+nsOfflineCacheUpdateService::nsOfflineCacheUpdateService()
+ : mDisabled(PR_FALSE)
+ , mUpdateRunning(PR_FALSE)
+{
+}
+
+nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService()
+{
+ gOfflineCacheUpdateService = nsnull;
+}
+
+nsresult
+nsOfflineCacheUpdateService::Init()
+{
+ nsresult rv;
+
+#if defined(PR_LOGGING)
+ if (!gOfflineCacheUpdateLog)
+ gOfflineCacheUpdateLog = PR_NewLogModule("nsOfflineCacheUpdate");
+#endif
+
+ if (!mDocUpdates.Init())
+ return NS_ERROR_FAILURE;
+
+ // Observe xpcom-shutdown event
+ nsCOMPtr<nsIObserverService> observerService =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = observerService->AddObserver(this,
+ NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ PR_TRUE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Register as an observer for the document loader
+ nsCOMPtr<nsIWebProgress> progress =
+ do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
+ if (progress) {
+ nsresult rv = progress->AddProgressListener
+ (this, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ gOfflineCacheUpdateService = this;
+
+ return NS_OK;
+}
+
+nsOfflineCacheUpdateService *
+nsOfflineCacheUpdateService::GetInstance()
+{
+ if (!gOfflineCacheUpdateService) {
+ gOfflineCacheUpdateService = new nsOfflineCacheUpdateService();
+ if (!gOfflineCacheUpdateService)
+ return nsnull;
+ NS_ADDREF(gOfflineCacheUpdateService);
+ nsresult rv = gOfflineCacheUpdateService->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(gOfflineCacheUpdateService);
+ return nsnull;
+ }
+ return gOfflineCacheUpdateService;
+ }
+
+ NS_ADDREF(gOfflineCacheUpdateService);
+
+ return gOfflineCacheUpdateService;
+}
+
+nsresult
+nsOfflineCacheUpdateService::Schedule(nsOfflineCacheUpdate *aUpdate)
+{
+ LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]",
+ this, aUpdate));
+
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> observerService =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(aUpdate),
+ "offline-cache-update-added",
+ nsnull);
+
+ mUpdates.AppendElement(aUpdate);
+
+ ProcessNextUpdate();
+
+ return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsOfflineCacheUpdate *aUpdate,
+ nsIDOMDocument *aDocument)
+{
+ LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, update=%p, doc=%p]",
+ this, aUpdate, aDocument));
+
+ if (!mDocUpdates.Put(aDocument, aUpdate))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdateService::UpdateFinished(nsOfflineCacheUpdate *aUpdate)
+{
+ LOG(("nsOfflineCacheUpdateService::UpdateFinished [%p, update=%p]",
+ this, aUpdate));
+
+ NS_ASSERTION(mUpdates.Length() > 0 &&
+ mUpdates[0] == aUpdate, "Unknown update completed");
+
+ // keep this item alive until we're done notifying observers
+ nsRefPtr<nsOfflineCacheUpdate> update = mUpdates[0];
+ mUpdates.RemoveElementAt(0);
+ mUpdateRunning = PR_FALSE;
+
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> observerService =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ observerService->NotifyObservers(static_cast<nsIOfflineCacheUpdate*>(aUpdate),
+ "offline-cache-update-completed",
+ nsnull);
+
+ ProcessNextUpdate();
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService <private>
+//-----------------------------------------------------------------------------
+
+nsresult
+nsOfflineCacheUpdateService::ProcessNextUpdate()
+{
+ LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%d]",
+ this, mUpdates.Length()));
+
+ if (mDisabled)
+ return NS_ERROR_ABORT;
+
+ if (mUpdateRunning)
+ return NS_OK;
+
+ if (mUpdates.Length() > 0) {
+ mUpdateRunning = PR_TRUE;
+ return mUpdates[0]->Begin();
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::GetNumUpdates(PRUint32 *aNumUpdates)
+{
+ LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this));
+
+ *aNumUpdates = mUpdates.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::GetUpdate(PRUint32 aIndex,
+ nsIOfflineCacheUpdate **aUpdate)
+{
+ LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex));
+
+ if (aIndex < mUpdates.Length()) {
+ NS_ADDREF(*aUpdate = mUpdates[aIndex]);
+ } else {
+ *aUpdate = nsnull;
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsIObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const PRUnichar *aData)
+{
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ if (mUpdates.Length() > 0)
+ mUpdates[0]->Cancel();
+ mDisabled = PR_TRUE;
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsIWebProgressListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OnProgressChange(nsIWebProgress *aProgress,
+ nsIRequest *aRequest,
+ PRInt32 curSelfProgress,
+ PRInt32 maxSelfProgress,
+ PRInt32 curTotalProgress,
+ PRInt32 maxTotalProgress)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OnStateChange(nsIWebProgress* aWebProgress,
+ nsIRequest *aRequest,
+ PRUint32 progressStateFlags,
+ nsresult aStatus)
+{
+ if ((progressStateFlags & STATE_IS_DOCUMENT) &&
+ (progressStateFlags & STATE_STOP)) {
+ if (mDocUpdates.Count() == 0)
+ return NS_OK;
+
+ nsCOMPtr<nsIDOMWindow> window;
+ aWebProgress->GetDOMWindow(getter_AddRefs(window));
+ if (!window) return NS_OK;
+
+ nsCOMPtr<nsIDOMDocument> doc;
+ window->GetDocument(getter_AddRefs(doc));
+ if (!doc) return NS_OK;
+
+ LOG(("nsOfflineCacheUpdateService::OnStateChange [%p, doc=%p]",
+ this, doc.get()));
+
+ nsRefPtr<nsOfflineCacheUpdate> update;
+ if (mDocUpdates.Get(doc, getter_AddRefs(update))) {
+ Schedule(update);
+ mDocUpdates.Remove(doc);
+ }
+
+ return NS_OK;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI *location)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const PRUnichar* aMessage)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OnSecurityChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ PRUint32 state)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/uriloader/prefetch/nsOfflineCacheUpdate.h
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * 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 ***** */
+
+#ifndef nsOfflineCacheUpdate_h__
+#define nsOfflineCacheUpdate_h__
+
+#include "nsIOfflineCacheUpdate.h"
+
+#include "nsAutoPtr.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsICacheService.h"
+#include "nsIChannelEventSink.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMLoadStatus.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIOfflineCacheSession.h"
+#include "nsIPrefetchService.h"
+#include "nsIRequestObserver.h"
+#include "nsIStreamListener.h"
+#include "nsIURI.h"
+#include "nsIWebProgressListener.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsWeakReference.h"
+
+class nsOfflineCacheUpdate;
+
+class nsOfflineCacheUpdateItem : public nsIDOMLoadStatus
+ , public nsIStreamListener
+ , public nsIInterfaceRequestor
+ , public nsIChannelEventSink
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMLOADSTATUS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSICHANNELEVENTSINK
+
+ nsOfflineCacheUpdateItem(nsOfflineCacheUpdate *aUpdate,
+ nsIURI *aURI,
+ nsIURI *aReferrerURI,
+ nsIDOMNode *aSource);
+ ~nsOfflineCacheUpdateItem();
+
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIURI> mReferrerURI;
+ nsCOMPtr<nsIWeakReference> mSource;
+
+ nsresult OpenChannel();
+ nsresult Cancel();
+
+private:
+ nsOfflineCacheUpdate* mUpdate;
+ nsCOMPtr<nsIChannel> mChannel;
+ PRUint16 mState;
+ PRInt32 mBytesRead;
+};
+
+class nsOfflineCacheUpdate : public nsIOfflineCacheUpdate
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOFFLINECACHEUPDATE
+
+ nsOfflineCacheUpdate();
+ ~nsOfflineCacheUpdate();
+
+ nsresult Init();
+
+ nsresult Begin();
+ nsresult Cancel();
+
+ void LoadCompleted();
+private:
+ nsresult ProcessNextURI();
+ nsresult AddOwnedItems(const nsACString &aOwnerURI);
+ nsresult AddDomainItems();
+ nsresult NotifyAdded(nsOfflineCacheUpdateItem *aItem);
+ nsresult NotifyCompleted(nsOfflineCacheUpdateItem *aItem);
+ nsresult Finish();
+
+ enum {
+ STATE_UNINITIALIZED,
+ STATE_INITIALIZED,
+ STATE_RUNNING,
+ STATE_CANCELLED,
+ STATE_FINISHED
+ } mState;
+
+ PRBool mAddedItems;
+ PRBool mPartialUpdate;
+ nsCString mUpdateDomain;
+ nsCString mOwnerURI;
+ nsCOMPtr<nsIURI> mReferrerURI;
+
+ nsCOMPtr<nsIOfflineCacheSession> mCacheSession;
+ nsCOMPtr<nsIObserverService> mObserverService;
+
+ /* Items being updated */
+ nsTArray<nsRefPtr<nsOfflineCacheUpdateItem> > mItems;
+
+ /* Clients watching this update for changes */
+ nsCOMArray<nsIWeakReference> mWeakObservers;
+ nsCOMArray<nsIOfflineCacheUpdateObserver> mObservers;
+};
+
+class nsOfflineCacheUpdateService : public nsIOfflineCacheUpdateService
+ , public nsIWebProgressListener
+ , public nsIObserver
+ , public nsSupportsWeakReference
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOFFLINECACHEUPDATESERVICE
+ NS_DECL_NSIWEBPROGRESSLISTENER
+ NS_DECL_NSIOBSERVER
+
+ nsOfflineCacheUpdateService();
+ ~nsOfflineCacheUpdateService();
+
+ nsresult Init();
+
+ nsresult Schedule(nsOfflineCacheUpdate *aUpdate);
+ nsresult ScheduleOnDocumentStop(nsOfflineCacheUpdate *aUpdate,
+ nsIDOMDocument *aDocument);
+ nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate);
+
+ static nsOfflineCacheUpdateService *GetInstance();
+
+private:
+ nsresult ProcessNextUpdate();
+
+ nsTArray<nsRefPtr<nsOfflineCacheUpdate> > mUpdates;
+ nsRefPtrHashtable<nsVoidPtrHashKey, nsOfflineCacheUpdate> mDocUpdates;
+
+ PRBool mDisabled;
+ PRBool mUpdateRunning;
+};
+
+#endif