From e3cf711385f2f6ff117bc07c43f4af093dff6cef Mon Sep 17 00:00:00 2001 From: Honza Bambas Date: Wed, 20 Oct 2010 19:12:32 +0200 Subject: [PATCH] Bug 536295 - e10s HTTP: offline application cache, r=dwitte, sr=cbiesinger, a=fennec-2.0b2+ --HG-- rename : uriloader/prefetch/nsOfflineCacheUpdate.cpp => uriloader/prefetch/OfflineCacheUpdateChild.cpp rename : uriloader/prefetch/nsOfflineCacheUpdate.cpp => uriloader/prefetch/nsOfflineCacheUpdateService.cpp --- browser/base/content/browser.js | 2 +- content/base/src/nsContentSink.cpp | 44 +- docshell/base/nsDocShell.cpp | 21 +- dom/base/nsGlobalWindow.cpp | 16 + dom/ipc/ContentChild.cpp | 1 + dom/ipc/PBrowser.ipdl | 29 + dom/ipc/TabChild.cpp | 20 + dom/ipc/TabChild.h | 6 + dom/ipc/TabParent.cpp | 30 + dom/ipc/TabParent.h | 7 + dom/src/offline/nsDOMOfflineResourceList.cpp | 206 +++-- dom/src/offline/nsDOMOfflineResourceList.h | 3 + ipc/ipdl/Makefile.in | 1 + netwerk/base/public/nsIApplicationCache.idl | 9 +- .../public/nsIApplicationCacheChannel.idl | 9 +- netwerk/build/nsNetCID.h | 13 + netwerk/build/nsNetModule.cpp | 5 + netwerk/cache/nsApplicationCache.h | 62 ++ netwerk/cache/nsDiskCacheDeviceSQL.cpp | 61 +- netwerk/protocol/http/HttpChannelChild.cpp | 74 +- netwerk/protocol/http/HttpChannelChild.h | 2 + netwerk/protocol/http/HttpChannelParent.cpp | 65 +- netwerk/protocol/http/HttpChannelParent.h | 5 +- netwerk/protocol/http/PHttpChannel.ipdl | 23 +- netwerk/protocol/http/nsHttpChannel.cpp | 19 + uriloader/prefetch/Makefile.in | 25 + .../prefetch/OfflineCacheUpdateChild.cpp | 533 ++++++++++++ uriloader/prefetch/OfflineCacheUpdateChild.h | 126 +++ uriloader/prefetch/OfflineCacheUpdateGlue.cpp | 234 +++++ uriloader/prefetch/OfflineCacheUpdateGlue.h | 104 +++ .../prefetch/OfflineCacheUpdateParent.cpp | 169 ++++ uriloader/prefetch/OfflineCacheUpdateParent.h | 72 ++ uriloader/prefetch/POfflineCacheUpdate.ipdl | 65 ++ uriloader/prefetch/ipdl.mk | 40 + uriloader/prefetch/nsIOfflineCacheUpdate.idl | 85 +- uriloader/prefetch/nsOfflineCacheUpdate.cpp | 821 ++---------------- uriloader/prefetch/nsOfflineCacheUpdate.h | 28 +- .../prefetch/nsOfflineCacheUpdateService.cpp | 612 +++++++++++++ 38 files changed, 2672 insertions(+), 975 deletions(-) create mode 100644 netwerk/cache/nsApplicationCache.h create mode 100644 uriloader/prefetch/OfflineCacheUpdateChild.cpp create mode 100644 uriloader/prefetch/OfflineCacheUpdateChild.h create mode 100644 uriloader/prefetch/OfflineCacheUpdateGlue.cpp create mode 100644 uriloader/prefetch/OfflineCacheUpdateGlue.h create mode 100644 uriloader/prefetch/OfflineCacheUpdateParent.cpp create mode 100644 uriloader/prefetch/OfflineCacheUpdateParent.h create mode 100644 uriloader/prefetch/POfflineCacheUpdate.ipdl create mode 100644 uriloader/prefetch/ipdl.mk create mode 100644 uriloader/prefetch/nsOfflineCacheUpdateService.cpp diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 1f940594a49..9a1337c0544 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -5735,7 +5735,7 @@ var OfflineApps = { var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]. getService(Ci.nsIOfflineCacheUpdateService); - updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject); + updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, window); }, ///////////////////////////////////////////////////////////////////////////// diff --git a/content/base/src/nsContentSink.cpp b/content/base/src/nsContentSink.cpp index cc7b9cc62f9..cd6a9213c48 100644 --- a/content/base/src/nsContentSink.cpp +++ b/content/base/src/nsContentSink.cpp @@ -74,7 +74,6 @@ #include "nsIApplicationCache.h" #include "nsIApplicationCacheContainer.h" #include "nsIApplicationCacheChannel.h" -#include "nsIApplicationCacheService.h" #include "nsIScriptSecurityManager.h" #include "nsIDOMLoadStatus.h" #include "nsICookieService.h" @@ -942,29 +941,6 @@ nsContentSink::PrefetchDNS(const nsAString &aHref) } } -nsresult -nsContentSink::GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey) -{ - aCacheKey.Truncate(); - - nsresult rv; - nsCOMPtr cachingChannel = do_QueryInterface(aChannel, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr cacheKey; - rv = cachingChannel->GetCacheKey(getter_AddRefs(cacheKey)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr cacheKeyString = - do_QueryInterface(cacheKey, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = cacheKeyString->GetData(aCacheKey); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - nsresult nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache, nsIURI *aManifestURI, @@ -994,17 +970,8 @@ nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache, NS_ENSURE_SUCCESS(rv, rv); if (!equal) { - // This is a foreign entry, mark it as such and force a reload to avoid - // loading the foreign entry. The next attempt will not choose this - // cache entry (because it has been marked foreign). - - nsCAutoString cachekey; - rv = GetChannelCacheKey(mDocument->GetChannel(), cachekey); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aLoadApplicationCache->MarkEntry(cachekey, - nsIApplicationCache::ITEM_FOREIGN); - NS_ENSURE_SUCCESS(rv, rv); + // This is a foreign entry, force a reload to avoid loading the foreign + // entry. The entry will be marked as foreign to avoid loading it again. *aAction = CACHE_SELECTION_RELOAD; } @@ -1219,6 +1186,13 @@ nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec) case CACHE_SELECTION_RELOAD: { // This situation occurs only for toplevel documents, see bottom // of SelectDocAppCache method. + // The document has been loaded from a different offline cache group than + // the manifest it refers to, i.e. this is a foreign entry, mark it as such + // and force a reload to avoid loading it. The next attempt will not + // choose it. + + applicationCacheChannel->MarkOfflineCacheEntryAsForeign(); + nsCOMPtr webNav = do_QueryInterface(mDocShell); webNav->Stop(nsIWebNavigation::STOP_ALL); diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index f79cfa0f073..8257979f25d 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -230,6 +230,10 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #include "nsIChannelPolicy.h" #include "nsIContentSecurityPolicy.h" +#ifdef MOZ_IPC +#include "nsXULAppAPI.h" +#endif + using namespace mozilla; // Number of documents currently loading @@ -5928,7 +5932,13 @@ nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel, nsCOMPtr appCacheChannel = do_QueryInterface(aNewChannel); if (appCacheChannel) { - appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI)); +#ifdef MOZ_IPC + // Permission will be checked in the parent process. + if (GeckoProcessType_Default != XRE_GetProcessType()) + appCacheChannel->SetChooseApplicationCache(PR_TRUE); + else +#endif + appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(newURI)); } if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) && @@ -8581,7 +8591,14 @@ nsDocShell::DoURILoad(nsIURI * aURI, // Loads with the correct permissions should check for a matching // application cache. - appCacheChannel->SetChooseApplicationCache(ShouldCheckAppCache(aURI)); +#ifdef MOZ_IPC + // Permission will be checked in the parent process + if (GeckoProcessType_Default != XRE_GetProcessType()) + appCacheChannel->SetChooseApplicationCache(PR_TRUE); + else +#endif + appCacheChannel->SetChooseApplicationCache( + ShouldCheckAppCache(aURI)); } // Make sure to give the caller a channel if we managed to create one diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 5b5ff532a11..6785e2eb26c 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -8164,6 +8164,22 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic, return NS_OK; } + if (!nsCRT::strcmp(aTopic, "offline-cache-update-added")) { + if (mApplicationCache) + return NS_OK; + + // Instantiate the application object now. It observes update belonging to + // this window's document and correctly updates the applicationCache object + // state. + nsCOMPtr applicationCache; + GetApplicationCache(getter_AddRefs(applicationCache)); + nsCOMPtr observer = do_QueryInterface(applicationCache); + if (observer) + observer->Observe(aSubject, aTopic, aData); + + return NS_OK; + } + NS_WARNING("unrecognized topic in nsGlobalWindow::Observe"); return NS_ERROR_FAILURE; } diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 85fa9ac2674..894bf821ffd 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -85,6 +85,7 @@ using namespace mozilla::ipc; using namespace mozilla::net; using namespace mozilla::places; +using namespace mozilla::docshell; namespace mozilla { namespace dom { diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index a76668ef62e..904e24bc4cc 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -44,6 +44,7 @@ include protocol PDocumentRendererShmem; include protocol PDocumentRendererNativeID; include protocol PContentPermissionRequest; include protocol PRenderFrame; +include protocol POfflineCacheUpdate; include "TabMessageUtils.h"; include "gfxMatrix.h"; @@ -73,6 +74,7 @@ rpc protocol PBrowser manages PDocumentRendererNativeID; manages PContentPermissionRequest; manages PRenderFrame; + manages POfflineCacheUpdate; both: AsyncMessage(nsString aMessage, nsString aJSON); @@ -187,6 +189,33 @@ parent: */ async PRenderFrame(); + /** + * Starts an offline application cache update. + * @param manifestURI + * URI of the manifest to fetch, the application cache group ID + * @param documentURI + * URI of the document that referred the manifest + * @param clientID + * The group cache version identifier to use + * @param stickDocument + * True if the update was initiated by a document load that referred + * a manifest. + * False if the update was initiated by applicationCache.update() call. + * + * Tells the update to carry the documentURI to a potential separate + * update of implicit (master) items. + * + * Why this argument? If the document was not found in an offline cache + * before load and refers a manifest and this manifest itself has not + * been changed since the last fetch, we will not do the application + * cache group update. But we must cache the document (identified by the + * documentURI). This argument will ensure that a previously uncached + * document will get cached and that we don't re-cache a document that + * has already been cached (stickDocument=false). + */ + POfflineCacheUpdate(URI manifestURI, URI documentURI, nsCString clientID, + bool stickDocument); + __delete__(); child: diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index c0d7e6f8a6e..d9446d9e66c 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -41,6 +41,7 @@ #include "mozilla/dom/PContentDialogChild.h" #include "mozilla/layers/PLayersChild.h" #include "mozilla/layout/RenderFrameChild.h" +#include "mozilla/docshell/OfflineCacheUpdateChild.h" #include "BasicLayers.h" #include "nsIWebBrowser.h" @@ -94,6 +95,7 @@ using namespace mozilla::dom; using namespace mozilla::layers; using namespace mozilla::layout; +using namespace mozilla::docshell; NS_IMPL_ISUPPORTS1(ContentListener, nsIDOMEventListener) @@ -809,6 +811,24 @@ TabChild::RecvActivateFrameEvent(const nsString& aType, const bool& capture) return true; } +POfflineCacheUpdateChild* +TabChild::AllocPOfflineCacheUpdate(const URI& manifestURI, + const URI& documentURI, + const nsCString& clientID, + const bool& stickDocument) +{ + NS_RUNTIMEABORT("unused"); + return nsnull; +} + +bool +TabChild::DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* actor) +{ + OfflineCacheUpdateChild* offlineCacheUpdate = static_cast(actor); + delete offlineCacheUpdate; + return true; +} + bool TabChild::RecvLoadRemoteScript(const nsString& aURL) { diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 33e176c6416..807f6f34b18 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -277,6 +277,12 @@ public: virtual PContentPermissionRequestChild* AllocPContentPermissionRequest(const nsCString& aType, const IPC::URI& uri); virtual bool DeallocPContentPermissionRequest(PContentPermissionRequestChild* actor); + virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdate(const URI& manifestURI, + const URI& documentURI, + const nsCString& clientID, + const bool& stickDocument); + virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* offlineCacheUpdate); + nsIWebNavigation* WebNavigation() { return mWebNav; } JSContext* GetJSContext() { return mCx; } diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 3caf097daff..7a828b02e63 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -43,6 +43,7 @@ #include "mozilla/ipc/DocumentRendererShmemParent.h" #include "mozilla/ipc/DocumentRendererNativeIDParent.h" #include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/docshell/OfflineCacheUpdateParent.h" #include "nsIURI.h" #include "nsFocusManager.h" @@ -703,6 +704,35 @@ TabParent::DeallocPRenderFrame(PRenderFrameParent* aFrame) return true; } +mozilla::docshell::POfflineCacheUpdateParent* +TabParent::AllocPOfflineCacheUpdate(const URI& aManifestURI, + const URI& aDocumentURI, + const nsCString& aClientID, + const bool& stickDocument) +{ + nsRefPtr update = + new mozilla::docshell::OfflineCacheUpdateParent(); + + nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aClientID, + stickDocument); + if (NS_FAILED(rv)) + return nsnull; + + POfflineCacheUpdateParent* result = update.get(); + update.forget(); + return result; +} + +bool +TabParent::DeallocPOfflineCacheUpdate(mozilla::docshell::POfflineCacheUpdateParent* actor) +{ + mozilla::docshell::OfflineCacheUpdateParent* update = + static_cast(actor); + + update->Release(); + return true; +} + PRBool TabParent::ShouldDelayDialogs() { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 3c73446f908..56afc49ded6 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -170,6 +170,13 @@ public: virtual PContentPermissionRequestParent* AllocPContentPermissionRequest(const nsCString& aType, const IPC::URI& uri); virtual bool DeallocPContentPermissionRequest(PContentPermissionRequestParent* actor); + virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdate( + const URI& aManifestURI, + const URI& aDocumentURI, + const nsCString& aClientID, + const bool& stickDocument); + virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateParent* actor); + JSBool GetGlobalJSObject(JSContext* cx, JSObject** globalp); NS_DECL_ISUPPORTS diff --git a/dom/src/offline/nsDOMOfflineResourceList.cpp b/dom/src/offline/nsDOMOfflineResourceList.cpp index 361777da795..a56aa7adc87 100644 --- a/dom/src/offline/nsDOMOfflineResourceList.cpp +++ b/dom/src/offline/nsDOMOfflineResourceList.cpp @@ -57,6 +57,15 @@ #include "nsIScriptGlobalObject.h" #include "nsIWebNavigation.h" +#ifdef MOZ_IPC +#include "nsXULAppAPI.h" +#define IS_CHILD_PROCESS() \ + (GeckoProcessType_Default != XRE_GetProcessType()) +#else +#define IS_CHILD_PROCESS() \ + (false) +#endif + // Event names #define CHECKING_STR "checking" @@ -137,6 +146,8 @@ nsDOMOfflineResourceList::nsDOMOfflineResourceList(nsIURI *aManifestURI, , mDocumentURI(aDocumentURI) , mCachedKeys(nsnull) , mCachedKeysCount(0) + , mExposeCacheUpdateStatus(true) + , mStatus(nsIDOMOfflineResourceList::IDLE) { mOwner = aWindow; mScriptContext = aScriptContext; @@ -170,26 +181,29 @@ nsDOMOfflineResourceList::Init() if (!innerURI) return NS_ERROR_FAILURE; - mApplicationCacheService = - do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // Check for in-progress cache updates - nsCOMPtr cacheUpdateService = - do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 numUpdates; - rv = cacheUpdateService->GetNumUpdates(&numUpdates); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < numUpdates; i++) { - nsCOMPtr cacheUpdate; - rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate)); + if (!IS_CHILD_PROCESS()) + { + mApplicationCacheService = + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - UpdateAdded(cacheUpdate); + // Check for in-progress cache updates + nsCOMPtr cacheUpdateService = + do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 numUpdates; + rv = cacheUpdateService->GetNumUpdates(&numUpdates); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < numUpdates; i++) { + nsCOMPtr cacheUpdate; + rv = cacheUpdateService->GetUpdate(i, getter_AddRefs(cacheUpdate)); + NS_ENSURE_SUCCESS(rv, rv); + + UpdateAdded(cacheUpdate); + NS_ENSURE_SUCCESS(rv, rv); + } } // watch for new offline cache updates @@ -235,6 +249,9 @@ nsDOMOfflineResourceList::Disconnect() NS_IMETHODIMP nsDOMOfflineResourceList::GetMozItems(nsIDOMDOMStringList **aItems) { + if (IS_CHILD_PROCESS()) + return NS_ERROR_NOT_IMPLEMENTED; + *aItems = nsnull; nsRefPtr items = new nsDOMStringList(); @@ -270,6 +287,9 @@ nsDOMOfflineResourceList::GetMozItems(nsIDOMDOMStringList **aItems) NS_IMETHODIMP nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI, PRBool* aExists) { + if (IS_CHILD_PROCESS()) + return NS_ERROR_NOT_IMPLEMENTED; + nsresult rv = Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -297,6 +317,9 @@ nsDOMOfflineResourceList::MozHasItem(const nsAString& aURI, PRBool* aExists) NS_IMETHODIMP nsDOMOfflineResourceList::GetMozLength(PRUint32 *aLength) { + if (IS_CHILD_PROCESS()) + return NS_ERROR_NOT_IMPLEMENTED; + if (!mManifestURI) { *aLength = 0; return NS_OK; @@ -315,6 +338,9 @@ nsDOMOfflineResourceList::GetMozLength(PRUint32 *aLength) NS_IMETHODIMP nsDOMOfflineResourceList::MozItem(PRUint32 aIndex, nsAString& aURI) { + if (IS_CHILD_PROCESS()) + return NS_ERROR_NOT_IMPLEMENTED; + nsresult rv = Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -334,6 +360,9 @@ nsDOMOfflineResourceList::MozItem(PRUint32 aIndex, nsAString& aURI) NS_IMETHODIMP nsDOMOfflineResourceList::MozAdd(const nsAString& aURI) { + if (IS_CHILD_PROCESS()) + return NS_ERROR_NOT_IMPLEMENTED; + nsresult rv = Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -398,6 +427,9 @@ nsDOMOfflineResourceList::MozAdd(const nsAString& aURI) NS_IMETHODIMP nsDOMOfflineResourceList::MozRemove(const nsAString& aURI) { + if (IS_CHILD_PROCESS()) + return NS_ERROR_NOT_IMPLEMENTED; + nsresult rv = Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -453,26 +485,14 @@ nsDOMOfflineResourceList::GetStatus(PRUint16 *aStatus) // If there is an update in process, use its status. - if (mCacheUpdate) { + if (mCacheUpdate && mExposeCacheUpdateStatus) { rv = mCacheUpdate->GetStatus(aStatus); if (NS_SUCCEEDED(rv) && *aStatus != nsIDOMOfflineResourceList::IDLE) { return NS_OK; } } - nsCOMPtr activeCache; - rv = mApplicationCacheService->GetActiveCache(mManifestSpec, - getter_AddRefs(activeCache)); - NS_ENSURE_SUCCESS(rv, rv); - - if (activeCache == nsnull) { - *aStatus = nsIDOMOfflineResourceList::OBSOLETE; - } else if (appCache == activeCache) { - *aStatus = nsIDOMOfflineResourceList::IDLE; - } else { - *aStatus = nsIDOMOfflineResourceList::UPDATEREADY; - } - + *aStatus = mStatus; return NS_OK; } @@ -490,9 +510,12 @@ nsDOMOfflineResourceList::Update() do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr window = + do_QueryInterface(mOwner); + nsCOMPtr update; rv = updateService->ScheduleUpdate(mManifestURI, mDocumentURI, - getter_AddRefs(update)); + window, getter_AddRefs(update)); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; @@ -508,33 +531,39 @@ nsDOMOfflineResourceList::SwapCache() return NS_ERROR_DOM_SECURITY_ERR; } - nsCOMPtr serv = - do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr currentAppCache = GetDocumentAppCache(); - - nsCOMPtr newAppCache; - rv = serv->GetActiveCache(mManifestSpec, getter_AddRefs(newAppCache)); - NS_ENSURE_SUCCESS(rv, rv); - - // In the case of an obsolete cache group, newAppCache might be null. - // We will disassociate from the cache in that case. - - if (newAppCache == currentAppCache) { + if (!currentAppCache) { return NS_ERROR_DOM_INVALID_STATE_ERR; } + // Check the current and potentially newly available cache are not identical. + if (mAvailableApplicationCache == currentAppCache) { + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + + if (mAvailableApplicationCache) { + nsCString currClientId, availClientId; + currentAppCache->GetClientID(currClientId); + mAvailableApplicationCache->GetClientID(availClientId); + if (availClientId == currClientId) + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + ClearCachedKeys(); nsCOMPtr appCacheContainer = GetDocumentAppCacheContainer(); + // In the case of an obsolete cache group, newAppCache might be null. + // We will disassociate from the cache in that case. if (appCacheContainer) { - rv = appCacheContainer->SetApplicationCache(newAppCache); + rv = appCacheContainer->SetApplicationCache(mAvailableApplicationCache); NS_ENSURE_SUCCESS(rv, rv); } + mAvailableApplicationCache = nsnull; + mStatus = nsIDOMOfflineResourceList::IDLE; + return NS_OK; } @@ -727,53 +756,50 @@ nsDOMOfflineResourceList::Observe(nsISupports *aSubject, // nsDOMOfflineResourceList::nsIOfflineCacheUpdateObserver // NS_IMETHODIMP -nsDOMOfflineResourceList::Error(nsIOfflineCacheUpdate *aUpdate) +nsDOMOfflineResourceList::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, + PRUint32 event) { - SendEvent(NS_LITERAL_STRING(ERROR_STR)); + mExposeCacheUpdateStatus = + (event == STATE_CHECKING) || + (event == STATE_DOWNLOADING) || + (event == STATE_ITEMSTARTED) || + (event == STATE_ITEMCOMPLETED) || + // During notification of "obsolete" we must expose state of the update + (event == STATE_OBSOLETE); + + switch (event) { + case STATE_ERROR: + SendEvent(NS_LITERAL_STRING(ERROR_STR)); + break; + case STATE_CHECKING: + SendEvent(NS_LITERAL_STRING(CHECKING_STR)); + break; + case STATE_NOUPDATE: + SendEvent(NS_LITERAL_STRING(NOUPDATE_STR)); + break; + case STATE_OBSOLETE: + mStatus = nsIDOMOfflineResourceList::OBSOLETE; + mAvailableApplicationCache = nsnull; + SendEvent(NS_LITERAL_STRING(OBSOLETE_STR)); + break; + case STATE_DOWNLOADING: + SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR)); + break; + case STATE_ITEMSTARTED: + SendEvent(NS_LITERAL_STRING(PROGRESS_STR)); + break; + case STATE_ITEMCOMPLETED: + // Nothing to do here... + break; + } return NS_OK; } NS_IMETHODIMP -nsDOMOfflineResourceList::Checking(nsIOfflineCacheUpdate *aUpdate) +nsDOMOfflineResourceList::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache) { - SendEvent(NS_LITERAL_STRING(CHECKING_STR)); - return NS_OK; -} - -NS_IMETHODIMP -nsDOMOfflineResourceList::NoUpdate(nsIOfflineCacheUpdate *aUpdate) -{ - SendEvent(NS_LITERAL_STRING(NOUPDATE_STR)); - return NS_OK; -} - -NS_IMETHODIMP -nsDOMOfflineResourceList::Downloading(nsIOfflineCacheUpdate *aUpdate) -{ - SendEvent(NS_LITERAL_STRING(DOWNLOADING_STR)); - return NS_OK; -} - -NS_IMETHODIMP -nsDOMOfflineResourceList::ItemStarted(nsIOfflineCacheUpdate *aUpdate, - nsIDOMLoadStatus *aItem) -{ - SendEvent(NS_LITERAL_STRING(PROGRESS_STR)); - return NS_OK; -} - -NS_IMETHODIMP -nsDOMOfflineResourceList::ItemCompleted(nsIOfflineCacheUpdate *aUpdate, - nsIDOMLoadStatus *aItem) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsDOMOfflineResourceList::Obsolete(nsIOfflineCacheUpdate *aUpdate) -{ - SendEvent(NS_LITERAL_STRING(OBSOLETE_STR)); + mAvailableApplicationCache = aApplicationCache; return NS_OK; } @@ -875,8 +901,10 @@ nsDOMOfflineResourceList::UpdateCompleted(nsIOfflineCacheUpdate *aUpdate) if (NS_SUCCEEDED(rv) && succeeded && !partial) { if (isUpgrade) { + mStatus = nsIDOMOfflineResourceList::UPDATEREADY; SendEvent(NS_LITERAL_STRING(UPDATEREADY_STR)); } else { + mStatus = nsIDOMOfflineResourceList::IDLE; SendEvent(NS_LITERAL_STRING(CACHED_STR)); } } @@ -905,6 +933,9 @@ nsDOMOfflineResourceList::GetCacheKey(nsIURI *aURI, nsCString &aKey) nsresult nsDOMOfflineResourceList::CacheKeys() { + if (IS_CHILD_PROCESS()) + return NS_ERROR_NOT_IMPLEMENTED; + if (mCachedKeys) return NS_OK; @@ -929,6 +960,3 @@ nsDOMOfflineResourceList::ClearCachedKeys() mCachedKeysCount = 0; } } - - - diff --git a/dom/src/offline/nsDOMOfflineResourceList.h b/dom/src/offline/nsDOMOfflineResourceList.h index 2730f3f08f1..5983b8d18c3 100644 --- a/dom/src/offline/nsDOMOfflineResourceList.h +++ b/dom/src/offline/nsDOMOfflineResourceList.h @@ -110,7 +110,10 @@ private: nsCOMPtr mDocumentURI; nsCOMPtr mApplicationCacheService; + nsCOMPtr mAvailableApplicationCache; nsCOMPtr mCacheUpdate; + bool mExposeCacheUpdateStatus; + PRUint16 mStatus; // The set of dynamic keys for this application cache object. char **mCachedKeys; diff --git a/ipc/ipdl/Makefile.in b/ipc/ipdl/Makefile.in index 04770bd274a..bfa847b5706 100644 --- a/ipc/ipdl/Makefile.in +++ b/ipc/ipdl/Makefile.in @@ -67,6 +67,7 @@ IPDLDIRS = \ netwerk/protocol/http \ netwerk/protocol/wyciwyg \ netwerk/cookie \ + uriloader/prefetch \ $(NULL) ##----------------------------------------------------------------------------- diff --git a/netwerk/base/public/nsIApplicationCache.idl b/netwerk/base/public/nsIApplicationCache.idl index af8e53f1918..bdadddc627a 100644 --- a/netwerk/base/public/nsIApplicationCache.idl +++ b/netwerk/base/public/nsIApplicationCache.idl @@ -111,9 +111,16 @@ interface nsIApplicationCacheNamespace : nsISupports * loads. Inactive caches will be removed from the cache when they are * no longer referenced. */ -[scriptable, uuid(663e2e2e-04a0-47b6-87b3-a122be46cb53)] +[scriptable, uuid(32f83e3f-470c-4423-a86a-d35d1c215ccb)] interface nsIApplicationCache : nsISupports { + /** + * Init this application cache instance to just hold the group ID and + * the client ID to work just as a handle to the real cache. Used on + * content process to simplify the application cache code. + */ + void initAsHandle(in ACString groupId, in ACString clientId); + /** * Entries in an application cache can be marked as one or more of * the following types. diff --git a/netwerk/base/public/nsIApplicationCacheChannel.idl b/netwerk/base/public/nsIApplicationCacheChannel.idl index 9d40441b247..91ce01daf68 100644 --- a/netwerk/base/public/nsIApplicationCacheChannel.idl +++ b/netwerk/base/public/nsIApplicationCacheChannel.idl @@ -43,7 +43,7 @@ /** * Interface implemented by channels that support application caches. */ -[scriptable, uuid(9acfd21c-9c07-459f-8dae-ed2ffba23ddc)] +[scriptable, uuid(8d9024e6-ab01-442d-8119-2f55d55d91b0)] interface nsIApplicationCacheChannel : nsIApplicationCacheContainer { /** @@ -75,4 +75,11 @@ interface nsIApplicationCacheChannel : nsIApplicationCacheContainer * is called. */ attribute boolean chooseApplicationCache; + + /** + * A shortcut method to mark the cache item of this channel as 'foreign'. + * See the 'cache selection algorithm' and CACHE_SELECTION_RELOAD + * action handling in nsContentSink. + */ + void markOfflineCacheEntryAsForeign(); }; diff --git a/netwerk/build/nsNetCID.h b/netwerk/build/nsNetCID.h index 848be90a421..a6b6797261c 100644 --- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -512,6 +512,19 @@ {0x8e, 0x1c, 0xd1, 0xaf, 0x79, 0xdf, 0xd1, 0x2f} \ } +#define NS_APPLICATIONCACHE_CLASSNAME \ + "nsApplicationCache" +#define NS_APPLICATIONCACHE_CONTRACTID \ + "@mozilla.org/network/application-cache;1" + +#define NS_APPLICATIONCACHE_CID \ +{ /* 463440c5-baad-4f3c-9e50-0b107abe7183 */ \ + 0x463440c5, \ + 0xbaad, \ + 0x4f3c, \ + {0x9e, 0x50, 0xb, 0x10, 0x7a, 0xbe, 0x71, 0x83 } \ +} + /****************************************************************************** * netwerk/protocol/http/ classes */ diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 7df4b652ea5..c624de2aed1 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -61,6 +61,7 @@ #include "nsSOCKSSocketProvider.h" #include "nsCacheService.h" #include "nsDiskCacheDeviceSQL.h" +#include "nsApplicationCache.h" #include "nsMimeTypes.h" #include "nsNetStrings.h" #include "nsDNSPrefetch.h" @@ -211,6 +212,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsAboutCacheEntry) #ifdef NECKO_OFFLINE_CACHE NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsOfflineCacheDevice, nsOfflineCacheDevice::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR(nsApplicationCacheNamespace) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsApplicationCache) #endif #ifdef NECKO_PROTOCOL_file @@ -709,6 +711,7 @@ NS_DEFINE_NAMED_CID(NS_CACHESERVICE_CID); #ifdef NECKO_OFFLINE_CACHE NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHESERVICE_CID); NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHENAMESPACE_CID); +NS_DEFINE_NAMED_CID(NS_APPLICATIONCACHE_CID); #endif #ifdef NECKO_COOKIES NS_DEFINE_NAMED_CID(NS_COOKIEMANAGER_CID); @@ -833,6 +836,7 @@ static const mozilla::Module::CIDEntry kNeckoCIDs[] = { #ifdef NECKO_OFFLINE_CACHE { &kNS_APPLICATIONCACHESERVICE_CID, false, NULL, nsOfflineCacheDeviceConstructor }, { &kNS_APPLICATIONCACHENAMESPACE_CID, false, NULL, nsApplicationCacheNamespaceConstructor }, + { &kNS_APPLICATIONCACHE_CID, false, NULL, nsApplicationCacheConstructor }, #endif #ifdef NECKO_COOKIES { &kNS_COOKIEMANAGER_CID, false, NULL, nsICookieServiceConstructor }, @@ -964,6 +968,7 @@ static const mozilla::Module::ContractIDEntry kNeckoContracts[] = { #ifdef NECKO_OFFLINE_CACHE { NS_APPLICATIONCACHESERVICE_CONTRACTID, &kNS_APPLICATIONCACHESERVICE_CID }, { NS_APPLICATIONCACHENAMESPACE_CONTRACTID, &kNS_APPLICATIONCACHENAMESPACE_CID }, + { NS_APPLICATIONCACHE_CONTRACTID, &kNS_APPLICATIONCACHE_CID }, #endif #ifdef NECKO_COOKIES { NS_COOKIEMANAGER_CONTRACTID, &kNS_COOKIEMANAGER_CID }, diff --git a/netwerk/cache/nsApplicationCache.h b/netwerk/cache/nsApplicationCache.h new file mode 100644 index 00000000000..3da2db03f6f --- /dev/null +++ b/netwerk/cache/nsApplicationCache.h @@ -0,0 +1,62 @@ +/* vim:set ts=2 sw=2 sts=2 et cin: */ +/* ***** 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. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by Mozilla Corporation are Copyright (C) 2010 + * Mozilla Corporation. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Honza Bambas + * + * 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 ***** */ + +class nsApplicationCache : public nsIApplicationCache + , public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIAPPLICATIONCACHE + + nsApplicationCache(nsOfflineCacheDevice *device, + const nsACString &group, + const nsACString &clientID); + + nsApplicationCache(); + + virtual ~nsApplicationCache(); + + void MarkInvalid(); + +private: + nsRefPtr mDevice; + nsCString mGroup; + nsCString mClientID; + PRBool mValid; +}; + diff --git a/netwerk/cache/nsDiskCacheDeviceSQL.cpp b/netwerk/cache/nsDiskCacheDeviceSQL.cpp index 4aa9e28fe4a..fca7570e8d5 100644 --- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp +++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp @@ -41,6 +41,7 @@ #include "nsDiskCache.h" #include "nsDiskCacheDeviceSQL.h" #include "nsCacheService.h" +#include "nsApplicationCache.h" #include "nsNetCID.h" #include "nsNetUtil.h" @@ -594,32 +595,16 @@ nsApplicationCacheNamespace::GetData(nsACString &out) * nsApplicationCache */ -class nsApplicationCache : public nsIApplicationCache - , public nsSupportsWeakReference -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIAPPLICATIONCACHE - - nsApplicationCache(nsOfflineCacheDevice *device, - const nsACString &group, - const nsACString &clientID); - - virtual ~nsApplicationCache(); - - void MarkInvalid() { mValid = PR_FALSE; } - -private: - nsRefPtr mDevice; - nsCString mGroup; - nsCString mClientID; - PRBool mValid; -}; - NS_IMPL_ISUPPORTS2(nsApplicationCache, nsIApplicationCache, nsISupportsWeakReference) +nsApplicationCache::nsApplicationCache() + : mDevice(nsnull) + , mValid(PR_TRUE) +{ +} + nsApplicationCache::nsApplicationCache(nsOfflineCacheDevice *device, const nsACString &group, const nsACString &clientID) @@ -632,6 +617,9 @@ nsApplicationCache::nsApplicationCache(nsOfflineCacheDevice *device, nsApplicationCache::~nsApplicationCache() { + if (!mDevice) + return; + mDevice->mCaches.Remove(mClientID); // If this isn't an active cache anymore, it can be destroyed. @@ -639,6 +627,24 @@ nsApplicationCache::~nsApplicationCache() Discard(); } +void +nsApplicationCache::MarkInvalid() +{ + mValid = PR_FALSE; +} + +NS_IMETHODIMP +nsApplicationCache::InitAsHandle(const nsACString &groupId, + const nsACString &clientId) +{ + NS_ENSURE_FALSE(mDevice, NS_ERROR_ALREADY_INITIALIZED); + NS_ENSURE_TRUE(mGroup.IsEmpty(), NS_ERROR_ALREADY_INITIALIZED); + + mGroup = groupId; + mClientID = clientId; + return NS_OK; +} + NS_IMETHODIMP nsApplicationCache::GetGroupID(nsACString &out) { @@ -656,6 +662,8 @@ nsApplicationCache::GetClientID(nsACString &out) NS_IMETHODIMP nsApplicationCache::GetActive(PRBool *out) { + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); + *out = mDevice->IsActiveCache(mGroup, mClientID); return NS_OK; } @@ -664,6 +672,7 @@ NS_IMETHODIMP nsApplicationCache::Activate() { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); mDevice->ActivateCache(mGroup, mClientID); return NS_OK; @@ -673,6 +682,7 @@ NS_IMETHODIMP nsApplicationCache::Discard() { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); mValid = PR_FALSE; @@ -689,6 +699,7 @@ nsApplicationCache::MarkEntry(const nsACString &key, PRUint32 typeBits) { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); return mDevice->MarkEntry(mClientID, key, typeBits); } @@ -699,6 +710,7 @@ nsApplicationCache::UnmarkEntry(const nsACString &key, PRUint32 typeBits) { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); return mDevice->UnmarkEntry(mClientID, key, typeBits); } @@ -708,6 +720,7 @@ nsApplicationCache::GetTypes(const nsACString &key, PRUint32 *typeBits) { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); return mDevice->GetTypes(mClientID, key, typeBits); } @@ -718,6 +731,7 @@ nsApplicationCache::GatherEntries(PRUint32 typeBits, char *** keys) { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); return mDevice->GatherEntries(mClientID, typeBits, count, keys); } @@ -726,6 +740,7 @@ NS_IMETHODIMP nsApplicationCache::AddNamespaces(nsIArray *namespaces) { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); if (!namespaces) return NS_OK; @@ -757,6 +772,7 @@ nsApplicationCache::GetMatchingNamespace(const nsACString &key, { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); return mDevice->GetMatchingNamespace(mClientID, key, out); } @@ -765,6 +781,7 @@ NS_IMETHODIMP nsApplicationCache::GetUsage(PRUint32 *usage) { NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE); return mDevice->GetUsage(mClientID, usage); } diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index f5e3e4be8e0..76a27a67a00 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -184,6 +184,21 @@ class StartRequestEvent : public ChannelEvent nsCString mSecurityInfoSerialization; }; +bool +HttpChannelChild::RecvAssociateApplicationCache(const nsCString &groupID, + const nsCString &clientID) +{ + nsresult rv; + mApplicationCache = do_CreateInstance( + NS_APPLICATIONCACHE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return true; + + mLoadedFromApplicationCache = PR_TRUE; + mApplicationCache->InitAsHandle(groupID, clientID); + return true; +} + bool HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, const PRBool& useResponseHead, @@ -878,6 +893,22 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) return NS_OK; } + nsCString appCacheClientId; + if (mInheritApplicationCache) { + // Pick up an application cache from the notification + // callbacks if available + nsCOMPtr appCacheContainer; + GetCallback(appCacheContainer); + + if (appCacheContainer) { + nsCOMPtr appCache; + rv = appCacheContainer->GetApplicationCache(getter_AddRefs(appCache)); + if (NS_SUCCEEDED(rv) && appCache) { + appCache->GetClientID(appCacheClientId); + } + } + } + // // Send request to the chrome process... // @@ -902,7 +933,8 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) mRequestHeaders, mRequestHead.Method(), uploadStreamData, uploadStreamInfo, mPriority, mRedirectionLimit, mAllowPipelining, mForceAllowThirdPartyCookie, mSendResumeAt, - mStartPos, mEntityID); + mStartPos, mEntityID, mChooseApplicationCache, + appCacheClientId); return NS_OK; } @@ -1047,13 +1079,16 @@ HttpChannelChild::SetNewListener(nsIStreamListener *listener, NS_IMETHODIMP HttpChannelChild::GetApplicationCache(nsIApplicationCache **aApplicationCache) { - DROP_DEAD(); + NS_IF_ADDREF(*aApplicationCache = mApplicationCache); + return NS_OK; } NS_IMETHODIMP HttpChannelChild::SetApplicationCache(nsIApplicationCache *aApplicationCache) { - // FIXME: redirects call. so stub OK for now. Fix in bug 536295. - return NS_ERROR_NOT_IMPLEMENTED; + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); + + mApplicationCache = aApplicationCache; + return NS_OK; } //----------------------------------------------------------------------------- @@ -1061,34 +1096,43 @@ HttpChannelChild::SetApplicationCache(nsIApplicationCache *aApplicationCache) //----------------------------------------------------------------------------- NS_IMETHODIMP -HttpChannelChild::GetLoadedFromApplicationCache(PRBool *retval) +HttpChannelChild::GetLoadedFromApplicationCache(PRBool *aLoadedFromApplicationCache) { - // FIXME: stub for bug 536295 - *retval = 0; + *aLoadedFromApplicationCache = mLoadedFromApplicationCache; return NS_OK; } NS_IMETHODIMP -HttpChannelChild::GetInheritApplicationCache(PRBool *aInheritApplicationCache) +HttpChannelChild::GetInheritApplicationCache(PRBool *aInherit) { - DROP_DEAD(); + *aInherit = mInheritApplicationCache; + return NS_OK; } NS_IMETHODIMP -HttpChannelChild::SetInheritApplicationCache(PRBool aInheritApplicationCache) +HttpChannelChild::SetInheritApplicationCache(PRBool aInherit) { - // FIXME: Browser calls this early, so stub OK for now. Fix in bug 536295. + mInheritApplicationCache = aInherit; return NS_OK; } NS_IMETHODIMP -HttpChannelChild::GetChooseApplicationCache(PRBool *aChooseApplicationCache) +HttpChannelChild::GetChooseApplicationCache(PRBool *aChoose) { - DROP_DEAD(); + *aChoose = mChooseApplicationCache; + return NS_OK; } + NS_IMETHODIMP -HttpChannelChild::SetChooseApplicationCache(PRBool aChooseApplicationCache) +HttpChannelChild::SetChooseApplicationCache(PRBool aChoose) { - // FIXME: Browser calls this early, so stub OK for now. Fix in bug 536295. + mChooseApplicationCache = aChoose; + return NS_OK; +} + +NS_IMETHODIMP +HttpChannelChild::MarkOfflineCacheEntryAsForeign() +{ + SendMarkOfflineCacheEntryAsForeign(); return NS_OK; } diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index cbb7d4f7db4..eccd7181465 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -141,6 +141,8 @@ protected: const PRUint32& redirectFlags, const nsHttpResponseHead& responseHead); bool RecvRedirect3Complete(); + bool RecvAssociateApplicationCache(const nsCString& groupID, + const nsCString& clientID); bool RecvDeleteSelf(); bool GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nsnull); diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index dc9ec058517..fc104a857ee 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -54,6 +54,8 @@ #include "nsSerializationHelper.h" #include "nsISerializable.h" #include "nsIAssociatedContentSecurity.h" +#include "nsIApplicationCacheService.h" +#include "nsIOfflineCacheUpdate.h" namespace mozilla { namespace net { @@ -110,7 +112,9 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI, const PRBool& forceAllowThirdPartyCookie, const bool& doResumeAt, const PRUint64& startPos, - const nsCString& entityID) + const nsCString& entityID, + const bool& chooseApplicationCache, + const nsCString& appCacheClientID) { nsCOMPtr uri(aURI); nsCOMPtr originalUri(aOriginalURI); @@ -176,6 +180,41 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI, httpChan->SetAllowPipelining(allowPipelining); httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie); + nsCOMPtr appCacheChan = + do_QueryInterface(mChannel); + nsCOMPtr appCacheService = + do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID); + + PRBool setChooseApplicationCache = chooseApplicationCache; + if (appCacheChan && appCacheService) { + // We might potentially want to drop this flag (that is TRUE by default) + // after we succefully associate the channel with an application cache + // reported by the channel child. Dropping it here may be too early. + appCacheChan->SetInheritApplicationCache(PR_FALSE); + if (!appCacheClientID.IsEmpty()) { + nsCOMPtr appCache; + rv = appCacheService->GetApplicationCache(appCacheClientID, + getter_AddRefs(appCache)); + if (NS_SUCCEEDED(rv)) { + appCacheChan->SetApplicationCache(appCache); + setChooseApplicationCache = PR_FALSE; + } + } + + if (setChooseApplicationCache) { + nsCOMPtr offlineUpdateService = + do_GetService("@mozilla.org/offlinecacheupdate-service;1", &rv); + if (NS_SUCCEEDED(rv)) { + rv = offlineUpdateService->OfflineAppAllowedForURI(uri, + nsnull, + &setChooseApplicationCache); + + if (setChooseApplicationCache && NS_SUCCEEDED(rv)) + appCacheChan->SetChooseApplicationCache(PR_TRUE); + } + } + } + rv = httpChan->AsyncOpen(mChannelListener, nsnull); if (NS_FAILED(rv)) return SendCancelEarly(rv); @@ -273,6 +312,14 @@ HttpChannelParent::RecvDocumentChannelCleanup() return true; } +bool +HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign() +{ + nsHttpChannel *httpChan = static_cast(mChannel.get()); + httpChan->MarkOfflineCacheEntryAsForeign(); + return true; +} + //----------------------------------------------------------------------------- // nsIRequestObserver and nsIStreamListener methods equivalents //----------------------------------------------------------------------------- @@ -296,6 +343,22 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) nsCString cachedCharset; chan->GetCacheTokenCachedCharset(cachedCharset); + PRBool loadedFromApplicationCache; + chan->GetLoadedFromApplicationCache(&loadedFromApplicationCache); + if (loadedFromApplicationCache) { + nsCOMPtr appCache; + chan->GetApplicationCache(getter_AddRefs(appCache)); + nsCString appCacheGroupId; + nsCString appCacheClientId; + appCache->GetGroupID(appCacheGroupId); + appCache->GetClientID(appCacheClientId); + if (mIPCClosed || + !SendAssociateApplicationCache(appCacheGroupId, appCacheClientId)) + { + return NS_ERROR_UNEXPECTED; + } + } + nsCOMPtr encodedChannel = do_QueryInterface(aRequest); if (encodedChannel) encodedChannel->SetApplyConversion(PR_FALSE); diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h index ccad423c9fa..8d4f3980eab 100644 --- a/netwerk/protocol/http/HttpChannelParent.h +++ b/netwerk/protocol/http/HttpChannelParent.h @@ -96,7 +96,9 @@ protected: const PRBool& forceAllowThirdPartyCookie, const bool& doResumeAt, const PRUint64& startPos, - const nsCString& entityID); + const nsCString& entityID, + const bool& chooseApplicationCache, + const nsCString& appCacheClientID); virtual bool RecvSetPriority(const PRUint16& priority); virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset); @@ -110,6 +112,7 @@ protected: const PRInt32& broken, const PRInt32& no); virtual bool RecvDocumentChannelCleanup(); + virtual bool RecvMarkOfflineCacheEntryAsForeign(); virtual void ActorDestroy(ActorDestroyReason why); diff --git a/netwerk/protocol/http/PHttpChannel.ipdl b/netwerk/protocol/http/PHttpChannel.ipdl index 629c3cfb4fd..821a1bbcdac 100644 --- a/netwerk/protocol/http/PHttpChannel.ipdl +++ b/netwerk/protocol/http/PHttpChannel.ipdl @@ -76,7 +76,9 @@ parent: PRBool forceAllowThirdPartyCookie, bool resumeAt, PRUint64 startPos, - nsCString entityID); + nsCString entityID, + bool chooseApplicationCache, + nsCString appCacheClientID); SetPriority(PRUint16 priority); @@ -99,6 +101,21 @@ parent: // partial cleanup on parent. DocumentChannelCleanup(); + // This might have to be sync. If this fails we must fail the document load + // to avoid endless loop. + // + // Explanation: the document loaded was loaded from the offline cache. But + // the cache group id (the manifest URL) of the cache group it was loaded + // from is different then the manifest the document refers to in the html + // tag. If we detect this during the cache selection algorithm, we must not + // load this document from the offline cache group it was just loaded from. + // Marking the cache entry as foreign in its cache group will prevent + // the document to load from the bad offline cache group. After it is marked, + // we reload the document to take the effect. If we fail to mark the entry + // as foreign, we will end up in the same situation and reload again and + // again, indefinitely. + MarkOfflineCacheEntryAsForeign(); + __delete__(); child: @@ -135,6 +152,10 @@ child: // Called if redirect successful so that child can complete setup. Redirect3Complete(); + // Associte the child with an application ids + AssociateApplicationCache(nsCString groupID, + nsCString clientID); + // Tell child to delete channel (all IPDL deletes must be done from child to // avoid races: see bug 591708). DeleteSelf(); diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 65c5aa37dc6..8688d0cbd87 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -4533,6 +4533,25 @@ nsHttpChannel::SetChooseApplicationCache(PRBool aChoose) return NS_OK; } +NS_IMETHODIMP +nsHttpChannel::MarkOfflineCacheEntryAsForeign() +{ + if (!mApplicationCache) + return NS_ERROR_NOT_AVAILABLE; + + nsresult rv; + + nsCAutoString cacheKey; + rv = GenerateCacheKey(mPostID, cacheKey); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mApplicationCache->MarkEntry(cacheKey, + nsIApplicationCache::ITEM_FOREIGN); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + //----------------------------------------------------------------------------- // nsHttpChannel::nsIAsyncVerifyRedirectCallback //----------------------------------------------------------------------------- diff --git a/uriloader/prefetch/Makefile.in b/uriloader/prefetch/Makefile.in index 659ce099c9e..cab1995ae18 100644 --- a/uriloader/prefetch/Makefile.in +++ b/uriloader/prefetch/Makefile.in @@ -46,22 +46,47 @@ MODULE = prefetch LIBRARY_NAME = prefetch_s LIBXUL_LIBRARY = 1 +ifdef MOZ_IPC +EXPORTS_NAMESPACES = mozilla/docshell + +EXPORTS_mozilla/docshell += \ + OfflineCacheUpdateParent.h \ + OfflineCacheUpdateChild.h \ + $(NULL) +endif + CPPSRCS = \ nsPrefetchService.cpp \ nsOfflineCacheUpdate.cpp \ + nsOfflineCacheUpdateService.cpp \ + OfflineCacheUpdateGlue.cpp \ $(NULL) +ifdef MOZ_IPC +CPPSRCS += \ + OfflineCacheUpdateChild.cpp \ + OfflineCacheUpdateParent.cpp +endif + XPIDLSRCS = \ nsIPrefetchService.idl \ nsIOfflineCacheUpdate.idl \ $(NULL) + EXPORTS = \ nsCPrefetchService.h \ $(NULL) +LOCAL_INCLUDES = \ + -I$(topsrcdir)/content/base/src \ + -I$(topsrcdir)/content/events/src \ + $(NULL) + # we don't want the shared lib, but we want to force the creation of a static lib. FORCE_STATIC_LIB = 1 +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk # vim: ts=4 sw=4 noexpandtab diff --git a/uriloader/prefetch/OfflineCacheUpdateChild.cpp b/uriloader/prefetch/OfflineCacheUpdateChild.cpp new file mode 100644 index 00000000000..2a8fc9509da --- /dev/null +++ b/uriloader/prefetch/OfflineCacheUpdateChild.cpp @@ -0,0 +1,533 @@ +/* -*- 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 + * + * 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 "OfflineCacheUpdateChild.h" +#include "nsOfflineCacheUpdate.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/TabChild.h" + +#include "nsIApplicationCacheContainer.h" +#include "nsIApplicationCacheChannel.h" +#include "nsIApplicationCacheService.h" +#include "nsIDocShell.h" +#include "nsIDocShellTreeItem.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDOMWindow.h" +#include "nsIDOMOfflineResourceList.h" +#include "nsIDocument.h" +#include "nsIObserverService.h" +#include "nsIURL.h" +#include "nsITabChild.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsStreamUtils.h" +#include "nsThreadUtils.h" +#include "nsProxyRelease.h" +#include "prlog.h" +#include "nsIAsyncVerifyRedirectCallback.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 +// +extern PRLogModuleInfo *gOfflineCacheUpdateLog; +#endif +#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) +#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) + +namespace mozilla { +namespace docshell { + +//----------------------------------------------------------------------------- +// OfflineCacheUpdateChild::nsISupports +//----------------------------------------------------------------------------- + +NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild) + NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(OfflineCacheUpdateChild) +NS_IMPL_RELEASE_WITH_DESTROY(OfflineCacheUpdateChild, RefcountHitZero()) + +void +OfflineCacheUpdateChild::RefcountHitZero() +{ + if (mIPCActivated) { + // ContentChild::DeallocPOfflineCacheUpdate will delete this + OfflineCacheUpdateChild::Send__delete__(this); + } else { + delete this; // we never opened IPDL channel + } +} + +//----------------------------------------------------------------------------- +// OfflineCacheUpdateChild +//----------------------------------------------------------------------------- + +OfflineCacheUpdateChild::OfflineCacheUpdateChild(nsIDOMWindow* aWindow) + : mState(STATE_UNINITIALIZED) + , mIsUpgrade(PR_FALSE) + , mIPCActivated(PR_FALSE) + , mWindow(aWindow) +{ +} + +OfflineCacheUpdateChild::~OfflineCacheUpdateChild() +{ + LOG(("OfflineCacheUpdateChild::~OfflineCacheUpdateChild [%p]", this)); +} + +nsresult +OfflineCacheUpdateChild::GatherObservers(nsCOMArray &aObservers) +{ + for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) { + nsCOMPtr observer = + do_QueryReferent(mWeakObservers[i]); + if (observer) + aObservers.AppendObject(observer); + else + mWeakObservers.RemoveObjectAt(i--); + } + + for (PRInt32 i = 0; i < mObservers.Count(); i++) { + aObservers.AppendObject(mObservers[i]); + } + + return NS_OK; +} + +void +OfflineCacheUpdateChild::SetDocument(nsIDOMDocument *aDocument) +{ + // The design is one document for one cache update on the content process. + NS_ASSERTION(!mDocument, "Setting more then a single document on a child offline cache update"); + + LOG(("Document %p added to update child %p", aDocument, this)); + + // Add document only if it was not loaded from an offline cache. + // If it were loaded from an offline cache then it has already + // been associated with it and must not be again cached as + // implicit (which are the reasons we collect documents here). + nsCOMPtr document = do_QueryInterface(aDocument); + if (!document) + return; + + nsIChannel* channel = document->GetChannel(); + nsCOMPtr appCacheChannel = + do_QueryInterface(channel); + if (!appCacheChannel) + return; + + PRBool loadedFromAppCache; + appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache); + if (loadedFromAppCache) + return; + + mDocument = aDocument; +} + +nsresult +OfflineCacheUpdateChild::AssociateDocument(nsIDOMDocument *aDocument, + nsIApplicationCache *aApplicationCache) +{ + // Check that the document that requested this update was + // previously associated with an application cache. If not, it + // should be associated with the new one. + nsCOMPtr container = + do_QueryInterface(aDocument); + if (!container) + return NS_OK; + + nsCOMPtr existingCache; + nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!existingCache) { +#if defined(PR_LOGGING) + if (LOG_ENABLED()) { + nsCAutoString clientID; + if (aApplicationCache) { + aApplicationCache->GetClientID(clientID); + } + LOG(("Update %p: associating app cache %s to document %p", + this, clientID.get(), aDocument)); + } +#endif + + rv = container->SetApplicationCache(aApplicationCache); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// OfflineCacheUpdateChild::nsIOfflineCacheUpdate +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +OfflineCacheUpdateChild::Init(nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsIDOMDocument *aDocument) +{ + nsresult rv; + + // Make sure the service has been initialized + nsOfflineCacheUpdateService* service = + nsOfflineCacheUpdateService::EnsureService(); + if (!service) + return NS_ERROR_FAILURE; + + LOG(("OfflineCacheUpdateChild::Init [%p]", this)); + + // Only http and https applications are supported. + PRBool match; + rv = aManifestURI->SchemeIs("http", &match); + NS_ENSURE_SUCCESS(rv, rv); + + if (!match) { + rv = aManifestURI->SchemeIs("https", &match); + NS_ENSURE_SUCCESS(rv, rv); + if (!match) + return NS_ERROR_ABORT; + } + + mManifestURI = aManifestURI; + + rv = mManifestURI->GetAsciiHost(mUpdateDomain); + NS_ENSURE_SUCCESS(rv, rv); + + mDocumentURI = aDocumentURI; + + mState = STATE_INITIALIZED; + + if (aDocument) + SetDocument(aDocument); + + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::InitPartial(nsIURI *aManifestURI, + const nsACString& clientID, + nsIURI *aDocumentURI) +{ + NS_NOTREACHED("Not expected to do partial offline cache updates" + " on the child process"); + // For now leaving this method, we may discover we need it. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::GetUpdateDomain(nsACString &aUpdateDomain) +{ + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); + + aUpdateDomain = mUpdateDomain; + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::GetStatus(PRUint16 *aStatus) +{ + switch (mState) { + case STATE_CHECKING : + *aStatus = nsIDOMOfflineResourceList::CHECKING; + return NS_OK; + case STATE_DOWNLOADING : + *aStatus = nsIDOMOfflineResourceList::DOWNLOADING; + return NS_OK; + default : + *aStatus = nsIDOMOfflineResourceList::IDLE; + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::GetPartial(PRBool *aPartial) +{ + *aPartial = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::GetManifestURI(nsIURI **aManifestURI) +{ + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); + + NS_IF_ADDREF(*aManifestURI = mManifestURI); + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::GetSucceeded(PRBool *aSucceeded) +{ + NS_ENSURE_TRUE(mState == STATE_FINISHED, NS_ERROR_NOT_AVAILABLE); + + *aSucceeded = mSucceeded; + + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::GetIsUpgrade(PRBool *aIsUpgrade) +{ + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); + + *aIsUpgrade = mIsUpgrade; + + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::AddDynamicURI(nsIURI *aURI) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::AddObserver(nsIOfflineCacheUpdateObserver *aObserver, + PRBool aHoldWeak) +{ + LOG(("OfflineCacheUpdateChild::AddObserver [%p]", this)); + + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); + + if (aHoldWeak) { + nsCOMPtr weakRef = do_GetWeakReference(aObserver); + mWeakObservers.AppendObject(weakRef); + } else { + mObservers.AppendObject(aObserver); + } + + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateChild::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) +{ + LOG(("OfflineCacheUpdateChild::RemoveObserver [%p]", this)); + + NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); + + for (PRInt32 i = 0; i < mWeakObservers.Count(); i++) { + nsCOMPtr 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 +OfflineCacheUpdateChild::Schedule() +{ + LOG(("OfflineCacheUpdateChild::Schedule [%p]", this)); + +#ifdef MOZ_IPC + NS_ASSERTION(mWindow, "Window must be provided to the offline cache update child"); +#endif + + nsCOMPtr piWindow = + do_QueryInterface(mWindow); + mWindow = nsnull; + + nsIDocShell *docshell = piWindow->GetDocShell(); + + nsCOMPtr item = do_QueryInterface(docshell); + if (!item) { + NS_WARNING("doc shell tree item is null"); + return NS_ERROR_FAILURE; + } + + nsCOMPtr owner; + item->GetTreeOwner(getter_AddRefs(owner)); + + nsCOMPtr tabchild = do_GetInterface(owner); + if (!tabchild) { + NS_WARNING("tab is null"); + return NS_ERROR_FAILURE; + } + + // because owner implements nsITabChild, we can assume that it is + // the one and only TabChild. + mozilla::dom::TabChild* child = static_cast(tabchild.get()); + + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (observerService) { + LOG(("Calling offline-cache-update-added")); + observerService->NotifyObservers(static_cast(this), + "offline-cache-update-added", + nsnull); + LOG(("Done offline-cache-update-added")); + } + + // mDocument is non-null if both: + // 1. this update was initiated by a document that referred a manifest + // 2. the document has not already been loaded from the application cache + // This tells the update to cache this document even in case the manifest + // has not been changed since the last fetch. + // See also nsOfflineCacheUpdate::ScheduleImplicit. + bool stickDocument = mDocument != nsnull; + + // Need to addref ourself here, because the IPC stack doesn't hold + // a reference to us. Will be released in RecvFinish() that identifies + // the work has been done. + child->SendPOfflineCacheUpdateConstructor(this, + IPC::URI(mManifestURI), + IPC::URI(mDocumentURI), + mClientID, + stickDocument); + + mIPCActivated = PR_TRUE; + this->AddRef(); + + return NS_OK; +} + +bool +OfflineCacheUpdateChild::RecvAssociateDocuments(const nsCString &cacheGroupId, + const nsCString &cacheClientId) +{ + LOG(("OfflineCacheUpdateChild::RecvAssociateDocuments [%p, cache=%s]", this, cacheClientId.get())); + + nsresult rv; + + nsCOMPtr cache = + do_CreateInstance(NS_APPLICATIONCACHE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return true; + + cache->InitAsHandle(cacheGroupId, cacheClientId); + + if (mDocument) { + AssociateDocument(mDocument, cache); + } + + nsCOMArray observers; + rv = GatherObservers(observers); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRInt32 i = 0; i < observers.Count(); i++) + observers[i]->ApplicationCacheAvailable(cache); + + return true; +} + +bool +OfflineCacheUpdateChild::RecvNotifyStateEvent(const PRUint32 &event) +{ + LOG(("OfflineCacheUpdateChild::RecvNotifyStateEvent [%p]", this)); + + // Convert the public observer state to our internal state + switch (event) { + case nsIOfflineCacheUpdateObserver::STATE_CHECKING: + mState = STATE_CHECKING; + break; + + case nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING: + mState = STATE_DOWNLOADING; + break; + + default: + break; + } + + nsCOMArray observers; + nsresult rv = GatherObservers(observers); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRInt32 i = 0; i < observers.Count(); i++) + observers[i]->UpdateStateChanged(this, event); + + return true; +} + +bool +OfflineCacheUpdateChild::RecvFinish(const bool &succeeded, + const bool &isUpgrade) +{ + LOG(("OfflineCacheUpdateChild::RecvFinish [%p]", this)); + + nsRefPtr kungFuDeathGrip(this); + + mState = STATE_FINISHED; + mSucceeded = succeeded; + mIsUpgrade = isUpgrade; + + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (observerService) { + LOG(("Calling offline-cache-update-completed")); + observerService->NotifyObservers(static_cast(this), + "offline-cache-update-completed", + nsnull); + LOG(("Done offline-cache-update-completed")); + } + + // This is by contract the last notification from the parent, release + // us now. This is corresponding to AddRef in Schedule(). + this->Release(); + + return true; +} + +} +} \ No newline at end of file diff --git a/uriloader/prefetch/OfflineCacheUpdateChild.h b/uriloader/prefetch/OfflineCacheUpdateChild.h new file mode 100644 index 00000000000..28eba828287 --- /dev/null +++ b/uriloader/prefetch/OfflineCacheUpdateChild.h @@ -0,0 +1,126 @@ +/* -*- 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) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Honza Bambas + * + * 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 nsOfflineCacheUpdateChild_h +#define nsOfflineCacheUpdateChild_h + +#include "mozilla/docshell/POfflineCacheUpdateChild.h" +#include "nsIOfflineCacheUpdate.h" + +#include "nsCOMArray.h" +#include "nsCOMPtr.h" +#include "nsICacheService.h" +#include "nsIDOMDocument.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsIURI.h" +#include "nsString.h" +#include "nsWeakReference.h" + +namespace mozilla { +namespace docshell { + +class OfflineCacheUpdateChild : public nsIOfflineCacheUpdate + , public POfflineCacheUpdateChild +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOFFLINECACHEUPDATE + + virtual bool + RecvNotifyStateEvent(const PRUint32& stateEvent); + + virtual bool + RecvAssociateDocuments( + const nsCString& cacheGroupId, + const nsCString& cacheClientId); + + virtual bool + RecvFinish(const bool& succeded, + const bool& isUpgrade); + + OfflineCacheUpdateChild(nsIDOMWindow* aWindow); + ~OfflineCacheUpdateChild(); + + void SetDocument(nsIDOMDocument *aDocument); + +private: + nsresult AssociateDocument(nsIDOMDocument *aDocument, + nsIApplicationCache *aApplicationCache); + nsresult GatherObservers(nsCOMArray &aObservers); + nsresult Finish(); + + void RefcountHitZero(); + + enum { + STATE_UNINITIALIZED, + STATE_INITIALIZED, + STATE_CHECKING, + STATE_DOWNLOADING, + STATE_CANCELLED, + STATE_FINISHED + } mState; + + PRPackedBool mIsUpgrade; + PRPackedBool mSucceeded; + PRPackedBool mIPCActivated; + + nsCString mUpdateDomain; + nsCOMPtr mManifestURI; + nsCOMPtr mDocumentURI; + + nsCString mClientID; + + nsCOMPtr mObserverService; + + /* Clients watching this update for changes */ + nsCOMArray mWeakObservers; + nsCOMArray mObservers; + + /* Document that requested this update */ + nsCOMPtr mDocument; + + /* Keep reference to the window that owns this update to call the + parent offline cache update construcor */ + nsCOMPtr mWindow; +}; + +} +} + +#endif diff --git a/uriloader/prefetch/OfflineCacheUpdateGlue.cpp b/uriloader/prefetch/OfflineCacheUpdateGlue.cpp new file mode 100644 index 00000000000..062741ea092 --- /dev/null +++ b/uriloader/prefetch/OfflineCacheUpdateGlue.cpp @@ -0,0 +1,234 @@ +/* -*- 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) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Honza Bambas + * + * 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 "OfflineCacheUpdateGlue.h" +#include "nsOfflineCacheUpdate.h" +#include "mozilla/Services.h" + +#include "nsIApplicationCache.h" +#include "nsIApplicationCacheChannel.h" +#include "nsIApplicationCacheContainer.h" +#include "nsIChannel.h" +#include "nsIDocument.h" +#include "prlog.h" + +#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 +// +extern PRLogModuleInfo *gOfflineCacheUpdateLog; +#endif +#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) +#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) + +namespace mozilla { +namespace docshell { + +//----------------------------------------------------------------------------- +// OfflineCacheUpdateGlue::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS3(OfflineCacheUpdateGlue, + nsIOfflineCacheUpdate, + nsIOfflineCacheUpdateObserver, + nsISupportsWeakReference) + +//----------------------------------------------------------------------------- +// OfflineCacheUpdateGlue +//----------------------------------------------------------------------------- + +OfflineCacheUpdateGlue::OfflineCacheUpdateGlue() +{ + LOG(("OfflineCacheUpdateGlue::OfflineCacheUpdateGlue [%p]", this)); +} + +OfflineCacheUpdateGlue::~OfflineCacheUpdateGlue() +{ + LOG(("OfflineCacheUpdateGlue::~OfflineCacheUpdateGlue [%p]", this)); +} + +nsIOfflineCacheUpdate* +OfflineCacheUpdateGlue::EnsureUpdate() +{ + if (!mUpdate) { + mUpdate = new nsOfflineCacheUpdate(); + LOG(("OfflineCacheUpdateGlue [%p] is using update [%p]", this, mUpdate.get())); + } + + return mUpdate; +} + +NS_IMETHODIMP +OfflineCacheUpdateGlue::Schedule() +{ + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (observerService) { + LOG(("Calling offline-cache-update-added")); + observerService->NotifyObservers(static_cast(this), + "offline-cache-update-added", + nsnull); + LOG(("Done offline-cache-update-added")); + } + + if (!EnsureUpdate()) + return NS_ERROR_NULL_POINTER; + + // Do not use weak reference, we must survive! + mUpdate->AddObserver(this, PR_FALSE); + + return mUpdate->Schedule(); +} + +NS_IMETHODIMP +OfflineCacheUpdateGlue::Init(nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsIDOMDocument *aDocument) +{ + if (!EnsureUpdate()) + return NS_ERROR_NULL_POINTER; + + mDocumentURI = aDocumentURI; + + if (aDocument) + SetDocument(aDocument); + + return mUpdate->Init(aManifestURI, aDocumentURI, nsnull); +} + +void +OfflineCacheUpdateGlue::SetDocument(nsIDOMDocument *aDocument) +{ + // The design is one document for one cache update on the content process. + NS_ASSERTION(!mDocument, + "Setting more then a single document on an instance of OfflineCacheUpdateGlue"); + + LOG(("Document %p added to update glue %p", aDocument, this)); + + // Add document only if it was not loaded from an offline cache. + // If it were loaded from an offline cache then it has already + // been associated with it and must not be again cached as + // implicit (which are the reasons we collect documents here). + nsCOMPtr document = do_QueryInterface(aDocument); + if (!document) + return; + + nsIChannel* channel = document->GetChannel(); + nsCOMPtr appCacheChannel = + do_QueryInterface(channel); + if (!appCacheChannel) + return; + + PRBool loadedFromAppCache; + appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache); + if (loadedFromAppCache) + return; + + if (EnsureUpdate()) { + mUpdate->StickDocument(mDocumentURI); + } + + mDocument = aDocument; +} + +NS_IMETHODIMP +OfflineCacheUpdateGlue::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, PRUint32 state) +{ + if (state == nsIOfflineCacheUpdateObserver::STATE_FINISHED) { + LOG(("OfflineCacheUpdateGlue got STATE_FINISHED [%p]", this)); + + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (observerService) { + LOG(("Calling offline-cache-update-completed")); + observerService->NotifyObservers(static_cast(this), + "offline-cache-update-completed", + nsnull); + LOG(("Done offline-cache-update-completed")); + } + + aUpdate->RemoveObserver(this); + } + + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateGlue::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache) +{ + NS_ENSURE_ARG(aApplicationCache); + + // Check that the document that requested this update was + // previously associated with an application cache. If not, it + // should be associated with the new one. + nsCOMPtr container = + do_QueryInterface(mDocument); + if (!container) + return NS_OK; + + nsCOMPtr existingCache; + nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!existingCache) { +#if defined(PR_LOGGING) + if (LOG_ENABLED()) { + nsCAutoString clientID; + if (aApplicationCache) { + aApplicationCache->GetClientID(clientID); + } + LOG(("Update %p: associating app cache %s to document %p", + this, clientID.get(), mDocument)); + } +#endif + + rv = container->SetApplicationCache(aApplicationCache); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +} +} diff --git a/uriloader/prefetch/OfflineCacheUpdateGlue.h b/uriloader/prefetch/OfflineCacheUpdateGlue.h new file mode 100644 index 00000000000..c66144d30ef --- /dev/null +++ b/uriloader/prefetch/OfflineCacheUpdateGlue.h @@ -0,0 +1,104 @@ +/* -*- 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) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Honza Bambas + * + * 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 nsOfflineCacheUpdateGlue_h +#define nsOfflineCacheUpdateGlue_h + +#include "nsIOfflineCacheUpdate.h" + +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsString.h" +#include "nsWeakReference.h" + +class nsOfflineCacheUpdate; + +namespace mozilla { +namespace docshell { + +// Like FORWARD_SAFE except methods: +// Schedule +// Init +#define NS_ADJUSTED_FORWARD_NSIOFFLINECACHEUPDATE(_to) \ + NS_SCRIPTABLE NS_IMETHOD GetStatus(PRUint16 *aStatus) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetStatus(aStatus); } \ + NS_SCRIPTABLE NS_IMETHOD GetPartial(PRBool *aPartial) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetPartial(aPartial); } \ + NS_SCRIPTABLE NS_IMETHOD GetIsUpgrade(PRBool *aIsUpgrade) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetIsUpgrade(aIsUpgrade); } \ + NS_SCRIPTABLE NS_IMETHOD GetUpdateDomain(nsACString & aUpdateDomain) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetUpdateDomain(aUpdateDomain); } \ + NS_SCRIPTABLE NS_IMETHOD GetManifestURI(nsIURI **aManifestURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetManifestURI(aManifestURI); } \ + NS_SCRIPTABLE NS_IMETHOD GetSucceeded(PRBool *aSucceeded) { return !_to ? NS_ERROR_NULL_POINTER : _to->GetSucceeded(aSucceeded); } \ + NS_SCRIPTABLE NS_IMETHOD InitPartial(nsIURI *aManifestURI, const nsACString & aClientID, nsIURI *aDocumentURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->InitPartial(aManifestURI, aClientID, aDocumentURI); } \ + NS_SCRIPTABLE NS_IMETHOD AddDynamicURI(nsIURI *aURI) { return !_to ? NS_ERROR_NULL_POINTER : _to->AddDynamicURI(aURI); } \ + NS_SCRIPTABLE NS_IMETHOD AddObserver(nsIOfflineCacheUpdateObserver *aObserver, PRBool aHoldWeak) { return !_to ? NS_ERROR_NULL_POINTER : _to->AddObserver(aObserver, aHoldWeak); } \ + NS_SCRIPTABLE NS_IMETHOD RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) { return !_to ? NS_ERROR_NULL_POINTER : _to->RemoveObserver(aObserver); } + +class OfflineCacheUpdateGlue : public nsSupportsWeakReference + , public nsIOfflineCacheUpdate + , public nsIOfflineCacheUpdateObserver +{ +public: + NS_DECL_ISUPPORTS + +private: + nsIOfflineCacheUpdate* EnsureUpdate(); + +public: + NS_ADJUSTED_FORWARD_NSIOFFLINECACHEUPDATE(EnsureUpdate()) + NS_SCRIPTABLE NS_IMETHOD Schedule(void); + NS_SCRIPTABLE NS_IMETHOD Init(nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsIDOMDocument *aDocument); + + NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER + + OfflineCacheUpdateGlue(); + ~OfflineCacheUpdateGlue(); + + void SetDocument(nsIDOMDocument *aDocument); + +private: + nsRefPtr mUpdate; + + /* Document that requested this update */ + nsCOMPtr mDocument; + nsCOMPtr mDocumentURI; +}; + +} +} + +#endif diff --git a/uriloader/prefetch/OfflineCacheUpdateParent.cpp b/uriloader/prefetch/OfflineCacheUpdateParent.cpp new file mode 100644 index 00000000000..d1b0839e805 --- /dev/null +++ b/uriloader/prefetch/OfflineCacheUpdateParent.cpp @@ -0,0 +1,169 @@ +/* -*- 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) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Honza Bambas + * + * 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 "OfflineCacheUpdateParent.h" +#include "nsOfflineCacheUpdate.h" +#include "nsIApplicationCache.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 +// +extern PRLogModuleInfo *gOfflineCacheUpdateLog; +#endif +#define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) +#define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) + +namespace mozilla { +namespace docshell { + +//----------------------------------------------------------------------------- +// OfflineCacheUpdateParent::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS1(OfflineCacheUpdateParent, + nsIOfflineCacheUpdateObserver) + +//----------------------------------------------------------------------------- +// OfflineCacheUpdateParent +//----------------------------------------------------------------------------- + +OfflineCacheUpdateParent::OfflineCacheUpdateParent() +{ + // Make sure the service has been initialized + nsOfflineCacheUpdateService* service = + nsOfflineCacheUpdateService::EnsureService(); + if (!service) + return; + + LOG(("OfflineCacheUpdateParent::OfflineCacheUpdateParent [%p]", this)); +} + +OfflineCacheUpdateParent::~OfflineCacheUpdateParent() +{ + LOG(("OfflineCacheUpdateParent::~OfflineCacheUpdateParent [%p]", this)); +} + +nsresult +OfflineCacheUpdateParent::Schedule(const URI& aManifestURI, + const URI& aDocumentURI, + const nsCString& aClientID, + const bool& stickDocument) +{ + LOG(("OfflineCacheUpdateParent::RecvSchedule [%p]", this)); + + nsRefPtr update; + nsCOMPtr manifestURI(aManifestURI); + nsCOMPtr documentURI(aDocumentURI); + + nsOfflineCacheUpdateService* service = + nsOfflineCacheUpdateService::EnsureService(); + if (!service) + return NS_ERROR_FAILURE; + + service->FindUpdate(manifestURI, documentURI, getter_AddRefs(update)); + if (!update) { + update = new nsOfflineCacheUpdate(); + + nsresult rv; + // Leave aDocument argument null. Only glues and children keep + // document instances. + rv = update->Init(manifestURI, documentURI, nsnull); + NS_ENSURE_SUCCESS(rv, rv); + + rv = update->Schedule(); + NS_ENSURE_SUCCESS(rv, rv); + } + + update->AddObserver(this, PR_FALSE); + + if (stickDocument) { + nsCOMPtr stickURI; + documentURI->Clone(getter_AddRefs(stickURI)); + update->StickDocument(stickURI); + } + + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateParent::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, PRUint32 state) +{ + LOG(("OfflineCacheUpdateParent::StateEvent [%p]", this)); + + SendNotifyStateEvent(state); + + if (state == nsIOfflineCacheUpdateObserver::STATE_FINISHED) { + // Tell the child the particulars after the update has finished. + // Sending the Finish event will release the child side of the protocol + // and notify "offline-cache-update-completed" on the child process. + PRBool isUpgrade; + aUpdate->GetIsUpgrade(&isUpgrade); + PRBool succeeded; + aUpdate->GetSucceeded(&succeeded); + + SendFinish(succeeded, isUpgrade); + } + + return NS_OK; +} + +NS_IMETHODIMP +OfflineCacheUpdateParent::ApplicationCacheAvailable(nsIApplicationCache *aApplicationCache) +{ + NS_ENSURE_ARG(aApplicationCache); + + nsCString cacheClientId; + aApplicationCache->GetClientID(cacheClientId); + nsCString cacheGroupId; + aApplicationCache->GetGroupID(cacheGroupId); + + SendAssociateDocuments(cacheGroupId, cacheClientId); + return NS_OK; +} + +} // docshell +} // mozilla \ No newline at end of file diff --git a/uriloader/prefetch/OfflineCacheUpdateParent.h b/uriloader/prefetch/OfflineCacheUpdateParent.h new file mode 100644 index 00000000000..247ebdca89c --- /dev/null +++ b/uriloader/prefetch/OfflineCacheUpdateParent.h @@ -0,0 +1,72 @@ +/* -*- 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) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Honza Bambas + * + * 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 nsOfflineCacheUpdateParent_h +#define nsOfflineCacheUpdateParent_h + +#include "mozilla/docshell/POfflineCacheUpdateParent.h" +#include "nsIOfflineCacheUpdate.h" + +#include "nsString.h" + +namespace mozilla { +namespace docshell { + +class OfflineCacheUpdateParent : public POfflineCacheUpdateParent + , public nsIOfflineCacheUpdateObserver +{ + NS_DECL_ISUPPORTS + NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER + + nsresult + Schedule(const URI& manifestURI, + const URI& documentURI, + const nsCString& clientID, + const bool& stickDocument); + + OfflineCacheUpdateParent(); + ~OfflineCacheUpdateParent(); + +private: + void RefcountHitZero(); +}; + +} +} + +#endif diff --git a/uriloader/prefetch/POfflineCacheUpdate.ipdl b/uriloader/prefetch/POfflineCacheUpdate.ipdl new file mode 100644 index 00000000000..9d9d0f0bad3 --- /dev/null +++ b/uriloader/prefetch/POfflineCacheUpdate.ipdl @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */ + +/* ***** 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 + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Honza Bambas + * + * 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 protocol PBrowser; + +include "mozilla/net/NeckoMessageUtils.h"; + +using IPC::URI; + +namespace mozilla { +namespace docshell { + +//------------------------------------------------------------------- +protocol POfflineCacheUpdate +{ + manager PBrowser; + +parent: + __delete__(); + +child: + NotifyStateEvent(PRUint32 stateEvent); + AssociateDocuments(nsCString cacheGroupId, nsCString cacheClientId); + Finish(bool succeded, bool isUpgrate); +}; + +} +} diff --git a/uriloader/prefetch/ipdl.mk b/uriloader/prefetch/ipdl.mk new file mode 100644 index 00000000000..414c4d94cdf --- /dev/null +++ b/uriloader/prefetch/ipdl.mk @@ -0,0 +1,40 @@ +# ***** 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 Firefox. +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): Jason Duell +# +# 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 ***** + +IPDLSRCS = \ + POfflineCacheUpdate.ipdl \ + $(NULL) + diff --git a/uriloader/prefetch/nsIOfflineCacheUpdate.idl b/uriloader/prefetch/nsIOfflineCacheUpdate.idl index bfec010ca29..63eee5dbf6f 100644 --- a/uriloader/prefetch/nsIOfflineCacheUpdate.idl +++ b/uriloader/prefetch/nsIOfflineCacheUpdate.idl @@ -39,76 +39,44 @@ #include "nsISupports.idl" interface nsIURI; +interface nsIDOMWindow; interface nsIDOMNode; interface nsIDOMDocument; interface nsIDOMLoadStatus; interface nsIOfflineCacheUpdate; interface nsIPrincipal; interface nsIPrefBranch; +interface nsIApplicationCache; -[scriptable, uuid(a28abeaf-a0b4-4440-b2fe-bc78249710ea)] +[scriptable, uuid(47360d57-8ef4-4a5d-8865-1a27a739ad1a)] interface nsIOfflineCacheUpdateObserver : nsISupports { - /** - * There was an error updating the cache. - * - * @param aUpdate - * The nsIOfflineCacheUpdate being processed. - */ - void error(in nsIOfflineCacheUpdate aUpdate); + const unsigned long STATE_ERROR = 1; + const unsigned long STATE_CHECKING = 2; + const unsigned long STATE_NOUPDATE = 3; + const unsigned long STATE_OBSOLETE = 4; + const unsigned long STATE_DOWNLOADING = 5; + const unsigned long STATE_ITEMSTARTED = 6; + const unsigned long STATE_ITEMCOMPLETED = 7; + const unsigned long STATE_FINISHED = 10; /** - * The manifest is being checked for updates + * aUpdate has changed its state. * * @param aUpdate * The nsIOfflineCacheUpdate being processed. + * @param event + * See enumeration above */ - void checking(in nsIOfflineCacheUpdate aUpdate); + void updateStateChanged(in nsIOfflineCacheUpdate aUpdate, in PRUint32 state); /** - * No update was necessary. + * Informs the observer about an application being available to associate. * - * @param aUpdate - * The nsIOfflineCacheUpdate being processed. + * @param applicationCache + * The application cache instance that has been created or found by the + * update to associate with */ - void noUpdate(in nsIOfflineCacheUpdate aUpdate); - - /** - * The cache group is now obsolete. - * - * @param aUpdate - * The nsIOfflineCacheUpdate being processed. - */ - void obsolete(in nsIOfflineCacheUpdate aUpdate); - - /** - * Starting to download resources - * - * @param aUpdate - * The nsIOfflineCacheUpdate being processed. - */ - void downloading(in nsIOfflineCacheUpdate aUpdate); - - /** - * An item has started downloading. - * - * @param aUpdate - * The nsIOfflineCacheUpdate being processed. - * @param aItem - * load status for the item that is being downloaded. - */ - void itemStarted(in nsIOfflineCacheUpdate aUpdate, - in nsIDOMLoadStatus aItem); - - /** - * An item has finished loading. - * - * @param aUpdate - * The nsIOfflineCacheUpdate being processed. - * @param aItem - * load status for the item that completed. - */ - void itemCompleted(in nsIOfflineCacheUpdate aUpdate, - in nsIDOMLoadStatus aItem); + void applicationCacheAvailable(in nsIApplicationCache applicationCache); }; /** @@ -125,7 +93,7 @@ interface nsIOfflineCacheUpdateObserver : nsISupports { * load its items one by one, sending itemCompleted() to any registered * observers. */ -[scriptable, uuid(877261bb-b952-4d27-847e-859bdd47c0ec)] +[scriptable, uuid(24605d81-8cf9-4021-8575-7f39aacbf31a)] interface nsIOfflineCacheUpdate : nsISupports { /** * Fetch the status of the running update. This will return a value @@ -169,7 +137,7 @@ interface nsIOfflineCacheUpdate : nsISupports { * @param aDocumentURI * The page that is requesting the update. */ - void init(in nsIURI aManifestURI, in nsIURI aDocumentURI); + void init(in nsIURI aManifestURI, in nsIURI aDocumentURI, in nsIDOMDocument aDocument); /** * Initialize the update for partial processing. @@ -200,12 +168,6 @@ interface nsIOfflineCacheUpdate : nsISupports { */ void schedule(); - /** - * 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. * @@ -254,7 +216,8 @@ interface nsIOfflineCacheUpdateService : nsISupports { * Otherwise a new update will be scheduled. */ nsIOfflineCacheUpdate scheduleUpdate(in nsIURI aManifestURI, - in nsIURI aDocumentURI); + in nsIURI aDocumentURI, + in nsIDOMWindow aWindow); /** * Schedule a cache update for a manifest when the document finishes diff --git a/uriloader/prefetch/nsOfflineCacheUpdate.cpp b/uriloader/prefetch/nsOfflineCacheUpdate.cpp index d411f837726..d72305e724d 100644 --- a/uriloader/prefetch/nsOfflineCacheUpdate.cpp +++ b/uriloader/prefetch/nsOfflineCacheUpdate.cpp @@ -86,7 +86,7 @@ static const PRUint32 kRescheduleLimit = 3; // this enables PR_LOG_ALWAYS level information and places all output in // the file offlineupdate.log // -static PRLogModuleInfo *gOfflineCacheUpdateLog; +extern PRLogModuleInfo *gOfflineCacheUpdateLog; #endif #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) @@ -1119,7 +1119,8 @@ nsOfflineManifestItem::OnStopRequest(nsIRequest *aRequest, // nsOfflineCacheUpdate::nsISupports //----------------------------------------------------------------------------- -NS_IMPL_ISUPPORTS1(nsOfflineCacheUpdate, +NS_IMPL_ISUPPORTS2(nsOfflineCacheUpdate, + nsIOfflineCacheUpdateObserver, nsIOfflineCacheUpdate) //----------------------------------------------------------------------------- @@ -1167,7 +1168,8 @@ nsOfflineCacheUpdate::GetCacheKey(nsIURI *aURI, nsACString &aKey) nsresult nsOfflineCacheUpdate::Init(nsIURI *aManifestURI, - nsIURI *aDocumentURI) + nsIURI *aDocumentURI, + nsIDOMDocument *aDocument) { nsresult rv; @@ -1361,9 +1363,9 @@ nsOfflineCacheUpdate::LoadCompleted() mSucceeded = PR_FALSE; mObsolete = PR_TRUE; if (mPreviousApplicationCache) { - NotifyObsolete(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_OBSOLETE); } else { - NotifyError(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); } Finish(); return; @@ -1372,7 +1374,7 @@ nsOfflineCacheUpdate::LoadCompleted() PRBool doUpdate; if (NS_FAILED(HandleManifest(&doUpdate))) { mSucceeded = PR_FALSE; - NotifyError(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); Finish(); return; } @@ -1380,16 +1382,14 @@ nsOfflineCacheUpdate::LoadCompleted() if (!doUpdate) { mSucceeded = PR_FALSE; - for (PRInt32 i = 0; i < mDocuments.Count(); i++) { - AssociateDocument(mDocuments[i], mPreviousApplicationCache); - } + AssociateDocuments(mPreviousApplicationCache); ScheduleImplicit(); // If we didn't need an implicit update, we can // send noupdate and end the update now. if (!mImplicitUpdate) { - NotifyNoUpdate(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE); Finish(); } return; @@ -1399,13 +1399,13 @@ nsOfflineCacheUpdate::LoadCompleted() mManifestItem->mItemType); if (NS_FAILED(rv)) { mSucceeded = PR_FALSE; - NotifyError(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); Finish(); return; } mState = STATE_DOWNLOADING; - NotifyDownloading(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING); // Start fetching resources. ProcessNextURI(); @@ -1437,12 +1437,12 @@ nsOfflineCacheUpdate::LoadCompleted() } if (!mSucceeded) { - NotifyError(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); Finish(); return; } - rv = NotifyCompleted(item); + rv = NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMCOMPLETED); if (NS_FAILED(rv)) return; ProcessNextURI(); @@ -1459,30 +1459,44 @@ nsOfflineCacheUpdate::ManifestCheckCompleted(nsresult aStatus, nsCAutoString firstManifestHash; mManifestItem->GetManifestHash(firstManifestHash); if (aManifestHash != firstManifestHash) { + LOG(("Manifest has changed during cache items download [%p]", this)); aStatus = NS_ERROR_FAILURE; } } if (NS_FAILED(aStatus)) { mSucceeded = PR_FALSE; - NotifyError(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); } - Finish(); - if (NS_FAILED(aStatus) && mRescheduleCount < kRescheduleLimit) { - // Reschedule this update. + // Do the final stuff but prevent notification of STATE_FINISHED. + // That would disconnect listeners that are responsible for document + // association after a successful update. Forwarding notifications + // from a new update through this dead update to them is absolutely + // correct. + FinishNoNotify(); + nsRefPtr newUpdate = new nsOfflineCacheUpdate(); - newUpdate->Init(mManifestURI, mDocumentURI); + // Leave aDocument argument null. Only glues and children keep + // document instances. + newUpdate->Init(mManifestURI, mDocumentURI, nsnull); - for (PRInt32 i = 0; i < mDocuments.Count(); i++) { - newUpdate->AddDocument(mDocuments[i]); + // In a rare case the manifest will not be modified on the next refetch + // transfer all master document URIs to the new update to ensure that + // all documents refering it will be properly cached. + for (PRUint32 i = 0; i < mDocumentURIs.Count(); i++) { + newUpdate->StickDocument(mDocumentURIs[i]); } newUpdate->mRescheduleCount = mRescheduleCount + 1; + newUpdate->AddObserver(this, PR_FALSE); newUpdate->Schedule(); } + else { + Finish(); + } } nsresult @@ -1497,7 +1511,7 @@ nsOfflineCacheUpdate::Begin() if (mPartialUpdate) { mState = STATE_DOWNLOADING; - NotifyDownloading(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_DOWNLOADING); ProcessNextURI(); return NS_OK; } @@ -1514,7 +1528,7 @@ nsOfflineCacheUpdate::Begin() } mState = STATE_CHECKING; - NotifyChecking(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_CHECKING); nsresult rv = mManifestItem->OpenChannel(); if (NS_FAILED(rv)) { @@ -1613,7 +1627,7 @@ nsOfflineCacheUpdate::ProcessNextURI() new nsManifestCheck(this, mManifestURI, mDocumentURI); if (NS_FAILED(manifestCheck->Begin())) { mSucceeded = PR_FALSE; - NotifyError(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); return Finish(); } @@ -1629,7 +1643,7 @@ nsOfflineCacheUpdate::ProcessNextURI() } #endif - NotifyStarted(mItems[mCurrentItem]); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ITEMSTARTED); nsresult rv = mItems[mCurrentItem]->OpenChannel(); if (NS_FAILED(rv)) { @@ -1660,146 +1674,42 @@ nsOfflineCacheUpdate::GatherObservers(nsCOMArray } nsresult -nsOfflineCacheUpdate::NotifyError() +nsOfflineCacheUpdate::NotifyState(PRUint32 state) { - LOG(("nsOfflineCacheUpdate::NotifyError [%p]", this)); - - mState = STATE_FINISHED; + LOG(("nsOfflineCacheUpdate::NotifyState [%p, %d]", this, state)); nsCOMArray observers; nsresult rv = GatherObservers(observers); NS_ENSURE_SUCCESS(rv, rv); for (PRInt32 i = 0; i < observers.Count(); i++) { - observers[i]->Error(this); + observers[i]->UpdateStateChanged(this, state); } return NS_OK; } nsresult -nsOfflineCacheUpdate::NotifyChecking() +nsOfflineCacheUpdate::AssociateDocuments(nsIApplicationCache* cache) { - LOG(("nsOfflineCacheUpdate::NotifyChecking [%p]", this)); - nsCOMArray observers; nsresult rv = GatherObservers(observers); NS_ENSURE_SUCCESS(rv, rv); for (PRInt32 i = 0; i < observers.Count(); i++) { - observers[i]->Checking(this); - } - - return NS_OK; -} - -nsresult -nsOfflineCacheUpdate::NotifyNoUpdate() -{ - LOG(("nsOfflineCacheUpdate::NotifyNoUpdate [%p]", this)); - - mState = STATE_FINISHED; - - nsCOMArray observers; - nsresult rv = GatherObservers(observers); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRInt32 i = 0; i < observers.Count(); i++) { - observers[i]->NoUpdate(this); - } - - return NS_OK; -} - -nsresult -nsOfflineCacheUpdate::NotifyObsolete() -{ - LOG(("nsOfflineCacheUpdate::NotifyObsolete [%p]", this)); - - mState = STATE_FINISHED; - - nsCOMArray observers; - nsresult rv = GatherObservers(observers); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRInt32 i = 0; i < observers.Count(); i++) { - observers[i]->Obsolete(this); - } - - return NS_OK; -} - -nsresult -nsOfflineCacheUpdate::NotifyDownloading() -{ - LOG(("nsOfflineCacheUpdate::NotifyDownloading [%p]", this)); - - nsCOMArray observers; - nsresult rv = GatherObservers(observers); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRInt32 i = 0; i < observers.Count(); i++) { - observers[i]->Downloading(this); - } - - return NS_OK; -} - -nsresult -nsOfflineCacheUpdate::NotifyStarted(nsOfflineCacheUpdateItem *aItem) -{ - LOG(("nsOfflineCacheUpdate::NotifyStarted [%p, %p]", this, aItem)); - - nsCOMArray observers; - nsresult rv = GatherObservers(observers); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRInt32 i = 0; i < observers.Count(); i++) { - observers[i]->ItemStarted(this, aItem); - } - - return NS_OK; -} - -nsresult -nsOfflineCacheUpdate::NotifyCompleted(nsOfflineCacheUpdateItem *aItem) -{ - LOG(("nsOfflineCacheUpdate::NotifyCompleted [%p, %p]", this, aItem)); - - nsCOMArray observers; - nsresult rv = GatherObservers(observers); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRInt32 i = 0; i < observers.Count(); i++) { - observers[i]->ItemCompleted(this, aItem); + observers[i]->ApplicationCacheAvailable(cache); } return NS_OK; } void -nsOfflineCacheUpdate::AddDocument(nsIDOMDocument *aDocument) +nsOfflineCacheUpdate::StickDocument(nsIURI *aDocumentURI) { - // Add document only if it was not loaded from an offline cache. - // If it were loaded from an offline cache then it has already - // been associated with it and must not be again cached as - // implicit (which are the reasons we collect documents here). - nsCOMPtr document = do_QueryInterface(aDocument); - if (!document) - return; + if (!aDocumentURI) + return; - nsIChannel* channel = document->GetChannel(); - nsCOMPtr appCacheChannel = - do_QueryInterface(channel); - if (!appCacheChannel) - return; - - PRBool loadedFromAppCache; - appCacheChannel->GetLoadedFromApplicationCache(&loadedFromAppCache); - if (loadedFromAppCache) - return; - - mDocuments.AppendObject(aDocument); + mDocumentURIs.AppendObject(aDocumentURI); } void @@ -1817,7 +1727,7 @@ nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate) mImplicitUpdate = nsnull; - NotifyNoUpdate(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_NOUPDATE); Finish(); return NS_OK; @@ -1826,7 +1736,7 @@ nsOfflineCacheUpdate::UpdateFinished(nsOfflineCacheUpdate *aUpdate) nsresult nsOfflineCacheUpdate::ScheduleImplicit() { - if (mDocuments.Count() == 0) + if (mDocumentURIs.Count() == 0) return NS_OK; nsresult rv; @@ -1846,41 +1756,12 @@ nsOfflineCacheUpdate::ScheduleImplicit() rv = update->InitPartial(mManifestURI, clientID, mDocumentURI); NS_ENSURE_SUCCESS(rv, rv); - PRBool added = PR_FALSE; - for (PRInt32 i = 0; i < mDocuments.Count(); i++) { - nsIDOMDocument* domDoc = mDocuments[i]; - nsCOMPtr doc = do_QueryInterface(domDoc); - if (!doc) - continue; - - nsIURI* uri = doc->GetDocumentURI(); - if (!uri) - continue; - - nsCOMPtr root = do_QueryInterface(doc->GetRootElement()); - if (!root) - continue; - - nsAutoString manifestSpec; - rv = root->GetAttribute(NS_LITERAL_STRING("manifest"), manifestSpec); + for (PRUint32 i = 0; i < mDocumentURIs.Count(); i++) { + rv = update->AddURI(mDocumentURIs[i], + nsIApplicationCache::ITEM_IMPLICIT); NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr manifestURI; - NS_NewURI(getter_AddRefs(manifestURI), manifestSpec, - doc->GetDocumentCharacterSet().get(), - doc->GetDocumentURI()); - if (!manifestURI) - continue; - - rv = update->AddURI(uri, nsIApplicationCache::ITEM_IMPLICIT); - NS_ENSURE_SUCCESS(rv, rv); - - added = PR_TRUE; } - if (!added) - return NS_OK; - update->SetOwner(this); rv = update->Begin(); NS_ENSURE_SUCCESS(rv, rv); @@ -1891,42 +1772,7 @@ nsOfflineCacheUpdate::ScheduleImplicit() } nsresult -nsOfflineCacheUpdate::AssociateDocument(nsIDOMDocument *aDocument, - nsIApplicationCache *aApplicationCache) -{ - // Check that the document that requested this update was - // previously associated with an application cache. If not, it - // should be associated with the new one. - nsCOMPtr container = - do_QueryInterface(aDocument); - if (!container) - return NS_OK; - - nsCOMPtr existingCache; - nsresult rv = container->GetApplicationCache(getter_AddRefs(existingCache)); - NS_ENSURE_SUCCESS(rv, rv); - - if (!existingCache) { -#if defined(PR_LOGGING) - if (LOG_ENABLED()) { - nsCAutoString clientID; - if (aApplicationCache) { - aApplicationCache->GetClientID(clientID); - } - LOG(("Update %p: associating app cache %s to document %p", - this, clientID.get(), aDocument)); - } -#endif - - rv = container->SetApplicationCache(aApplicationCache); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -nsOfflineCacheUpdate::Finish() +nsOfflineCacheUpdate::FinishNoNotify() { LOG(("nsOfflineCacheUpdate::Finish [%p]", this)); @@ -1937,19 +1783,17 @@ nsOfflineCacheUpdate::Finish() nsIArray *namespaces = mManifestItem->GetNamespaces(); nsresult rv = mApplicationCache->AddNamespaces(namespaces); if (NS_FAILED(rv)) { - NotifyError(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); mSucceeded = PR_FALSE; } rv = mApplicationCache->Activate(); if (NS_FAILED(rv)) { - NotifyError(); + NotifyState(nsIOfflineCacheUpdateObserver::STATE_ERROR); mSucceeded = PR_FALSE; } - for (PRInt32 i = 0; i < mDocuments.Count(); i++) { - AssociateDocument(mDocuments[i], mApplicationCache); - } + AssociateDocuments(mApplicationCache); } if (mObsolete) { @@ -1982,6 +1826,16 @@ nsOfflineCacheUpdate::Finish() return rv; } +nsresult +nsOfflineCacheUpdate::Finish() +{ + nsresult rv = FinishNoNotify(); + + NotifyState(nsIOfflineCacheUpdateObserver::STATE_FINISHED); + + return rv; +} + //----------------------------------------------------------------------------- // nsOfflineCacheUpdate::nsIOfflineCacheUpdate //----------------------------------------------------------------------------- @@ -2090,6 +1944,11 @@ nsOfflineCacheUpdate::AddURI(nsIURI *aURI, PRUint32 aType) NS_IMETHODIMP nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI) { +#if !defined(MOZ_IPC) + if (GeckoProcessType_Default != XRE_GetProcessType()) + return NS_ERROR_NOT_IMPLEMENTED; +#endif + // If this is a partial update and the resource is already in the // cache, we should only mark the entry, not fetch it again. if (mPartialUpdate) { @@ -2110,38 +1969,11 @@ nsOfflineCacheUpdate::AddDynamicURI(nsIURI *aURI) return AddURI(aURI, nsIApplicationCache::ITEM_DYNAMIC); } -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)); + LOG(("nsOfflineCacheUpdate::AddObserver [%p] to update [%p]", aObserver, this)); NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); @@ -2158,7 +1990,7 @@ nsOfflineCacheUpdate::AddObserver(nsIOfflineCacheUpdateObserver *aObserver, NS_IMETHODIMP nsOfflineCacheUpdate::RemoveObserver(nsIOfflineCacheUpdateObserver *aObserver) { - LOG(("nsOfflineCacheUpdate::RemoveObserver [%p]", this)); + LOG(("nsOfflineCacheUpdate::RemoveObserver [%p] from update [%p]", aObserver, this)); NS_ENSURE_TRUE(mState >= STATE_INITIALIZED, NS_ERROR_NOT_INITIALIZED); @@ -2194,504 +2026,31 @@ nsOfflineCacheUpdate::Schedule() return NS_ERROR_FAILURE; } - return service->Schedule(this); -} - -//----------------------------------------------------------------------------- -// nsOfflineCachePendingUpdate -//----------------------------------------------------------------------------- - -class nsOfflineCachePendingUpdate : public nsIWebProgressListener - , public nsSupportsWeakReference -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIWEBPROGRESSLISTENER - - nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService, - nsIURI *aManifestURI, - nsIURI *aDocumentURI, - nsIDOMDocument *aDocument) - : mService(aService) - , mManifestURI(aManifestURI) - , mDocumentURI(aDocumentURI) - { - mDocument = do_GetWeakReference(aDocument); - } - -private: - nsRefPtr mService; - nsCOMPtr mManifestURI; - nsCOMPtr mDocumentURI; - nsCOMPtr mDocument; -}; - -NS_IMPL_ISUPPORTS2(nsOfflineCachePendingUpdate, - nsIWebProgressListener, - nsISupportsWeakReference) - -//----------------------------------------------------------------------------- -// nsOfflineCacheUpdateService::nsIWebProgressListener -//----------------------------------------------------------------------------- - -NS_IMETHODIMP -nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress, - nsIRequest *aRequest, - PRInt32 curSelfProgress, - PRInt32 maxSelfProgress, - PRInt32 curTotalProgress, - PRInt32 maxTotalProgress) -{ - NS_NOTREACHED("notification excluded in AddProgressListener(...)"); - return NS_OK; + return service->ScheduleUpdate(this); } NS_IMETHODIMP -nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress, - nsIRequest *aRequest, - PRUint32 progressStateFlags, - nsresult aStatus) +nsOfflineCacheUpdate::UpdateStateChanged(nsIOfflineCacheUpdate *aUpdate, + PRUint32 aState) { - nsCOMPtr updateDoc = do_QueryReferent(mDocument); - if (!updateDoc) { - // The document that scheduled this update has gone away, - // we don't need to listen anymore. - aWebProgress->RemoveProgressListener(this); - NS_RELEASE_THIS(); - return NS_OK; + if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) { + // Take the mSucceeded flag from the underlying update, we will be + // queried for it soon. mSucceeded of this update is false (manifest + // check failed) but the subsequent re-fetch update might succeed + PRBool succeeded; + aUpdate->GetSucceeded(&succeeded); + mSucceeded = succeeded; } - if (!(progressStateFlags & STATE_STOP)) { - return NS_OK; - } + nsresult rv = NotifyState(aState); + if (aState == nsIOfflineCacheUpdateObserver::STATE_FINISHED) + aUpdate->RemoveObserver(this); - nsCOMPtr window; - aWebProgress->GetDOMWindow(getter_AddRefs(window)); - if (!window) return NS_OK; - - nsCOMPtr progressDoc; - window->GetDocument(getter_AddRefs(progressDoc)); - if (!progressDoc) return NS_OK; - - if (!SameCOMIdentity(progressDoc, updateDoc)) { - return NS_OK; - } - - LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]", - this, progressDoc.get())); - - // Only schedule the update if the document loaded successfully - if (NS_SUCCEEDED(aStatus)) { - nsCOMPtr update; - mService->Schedule(mManifestURI, mDocumentURI, - updateDoc, getter_AddRefs(update)); - } - - aWebProgress->RemoveProgressListener(this); - NS_RELEASE_THIS(); - - return NS_OK; + return rv; } NS_IMETHODIMP -nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - nsIURI *location) +nsOfflineCacheUpdate::ApplicationCacheAvailable(nsIApplicationCache *applicationCache) { - NS_NOTREACHED("notification excluded in AddProgressListener(...)"); - return NS_OK; -} - -NS_IMETHODIMP -nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress, - nsIRequest* aRequest, - nsresult aStatus, - const PRUnichar* aMessage) -{ - NS_NOTREACHED("notification excluded in AddProgressListener(...)"); - return NS_OK; -} - -NS_IMETHODIMP -nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - PRUint32 state) -{ - NS_NOTREACHED("notification excluded in AddProgressListener(...)"); - return NS_OK; -} - -//----------------------------------------------------------------------------- -// nsOfflineCacheUpdateService::nsISupports -//----------------------------------------------------------------------------- - -NS_IMPL_ISUPPORTS3(nsOfflineCacheUpdateService, - nsIOfflineCacheUpdateService, - nsIObserver, - nsISupportsWeakReference) - -//----------------------------------------------------------------------------- -// nsOfflineCacheUpdateService -//----------------------------------------------------------------------------- - -nsOfflineCacheUpdateService::nsOfflineCacheUpdateService() - : mDisabled(PR_FALSE) - , mUpdateRunning(PR_FALSE) -{ -} - -nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService() -{ - gOfflineCacheUpdateService = nsnull; -} - -nsresult -nsOfflineCacheUpdateService::Init() -{ -#if defined(PR_LOGGING) - if (!gOfflineCacheUpdateLog) - gOfflineCacheUpdateLog = PR_NewLogModule("nsOfflineCacheUpdate"); -#endif - - // Observe xpcom-shutdown event - nsCOMPtr observerService = - mozilla::services::GetObserverService(); - if (!observerService) - return NS_ERROR_FAILURE; - - nsresult rv = observerService->AddObserver(this, - NS_XPCOM_SHUTDOWN_OBSERVER_ID, - PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); - - gOfflineCacheUpdateService = this; - - return NS_OK; -} - -/* static */ -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; -} - -/* static */ -nsOfflineCacheUpdateService * -nsOfflineCacheUpdateService::EnsureService() -{ - if (!gOfflineCacheUpdateService) { - // Make the service manager hold a long-lived reference to the service - nsCOMPtr service = - do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID); - } - - return gOfflineCacheUpdateService; -} - -nsresult -nsOfflineCacheUpdateService::Schedule(nsOfflineCacheUpdate *aUpdate) -{ - LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]", - this, aUpdate)); - - aUpdate->SetOwner(this); - - nsCOMPtr observerService = - mozilla::services::GetObserverService(); - if (!observerService) - return NS_ERROR_FAILURE; - - observerService->NotifyObservers(static_cast(aUpdate), - "offline-cache-update-added", - nsnull); - - mUpdates.AppendElement(aUpdate); - - ProcessNextUpdate(); - - return NS_OK; -} - -NS_IMETHODIMP -nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI, - nsIURI *aDocumentURI, - nsIDOMDocument *aDocument) -{ - LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]", - this, aManifestURI, aDocumentURI, aDocument)); - - nsCOMPtr doc = do_QueryInterface(aDocument); - nsCOMPtr container = doc->GetContainer(); - nsCOMPtr progress = do_QueryInterface(container); - NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG); - - // Proceed with cache update - nsRefPtr update = - new nsOfflineCachePendingUpdate(this, aManifestURI, - aDocumentURI, aDocument); - NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY); - - nsresult rv = progress->AddProgressListener - (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT); - NS_ENSURE_SUCCESS(rv, rv); - - // The update will release when it has scheduled itself. - update.forget(); - - 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 update = mUpdates[0]; - mUpdates.RemoveElementAt(0); - mUpdateRunning = PR_FALSE; - - nsCOMPtr observerService = - mozilla::services::GetObserverService(); - if (!observerService) - return NS_ERROR_FAILURE; - - observerService->NotifyObservers(static_cast(aUpdate), - "offline-cache-update-completed", - nsnull); - - ProcessNextUpdate(); - - return NS_OK; -} - -//----------------------------------------------------------------------------- -// nsOfflineCacheUpdateService -//----------------------------------------------------------------------------- - -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; -} - -nsresult -nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI, - nsIURI *aDocumentURI, - nsIDOMDocument *aDocument, - nsIOfflineCacheUpdate **aUpdate) -{ - // Check for existing updates - nsresult rv; - for (PRUint32 i = 0; i < mUpdates.Length(); i++) { - nsRefPtr update = mUpdates[i]; - - PRBool partial; - rv = update->GetPartial(&partial); - NS_ENSURE_SUCCESS(rv, rv); - - if (partial) { - // Partial updates aren't considered - continue; - } - - nsCOMPtr manifestURI; - update->GetManifestURI(getter_AddRefs(manifestURI)); - if (manifestURI) { - PRBool equals; - rv = manifestURI->Equals(aManifestURI, &equals); - if (equals) { - if (aDocument) { - LOG(("Document %p added to update %p", aDocument, update.get())); - update->AddDocument(aDocument); - } - NS_ADDREF(*aUpdate = update); - return NS_OK; - } - } - } - - // There is no existing update, start one. - - nsRefPtr update = new nsOfflineCacheUpdate(); - if (!update) - return NS_ERROR_OUT_OF_MEMORY; - - rv = update->Init(aManifestURI, aDocumentURI); - NS_ENSURE_SUCCESS(rv, rv); - - if (aDocument) { - LOG(("First document %p added to update %p", aDocument, update.get())); - update->AddDocument(aDocument); - } - - rv = update->Schedule(); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ADDREF(*aUpdate = update); - - return NS_OK; -} - -NS_IMETHODIMP -nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI, - nsIURI *aDocumentURI, - nsIOfflineCacheUpdate **aUpdate) -{ - return Schedule(aManifestURI, aDocumentURI, nsnull, aUpdate); -} - -//----------------------------------------------------------------------------- -// 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::nsIOfflineCacheUpdateService -//----------------------------------------------------------------------------- - -NS_IMETHODIMP -nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal, - nsIPrefBranch *aPrefBranch, - PRBool *aAllowed) -{ - nsCOMPtr codebaseURI; - nsresult rv = aPrincipal->GetURI(getter_AddRefs(codebaseURI)); - NS_ENSURE_SUCCESS(rv, rv); - - return OfflineAppAllowedForURI(codebaseURI, aPrefBranch, aAllowed); -} - -NS_IMETHODIMP -nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI, - nsIPrefBranch *aPrefBranch, - PRBool *aAllowed) -{ - *aAllowed = PR_FALSE; - if (!aURI) - return NS_OK; - - nsCOMPtr innerURI = NS_GetInnermostURI(aURI); - if (!innerURI) - return NS_OK; - - // only http and https applications can use offline APIs. - PRBool match; - 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 permissionManager = - do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); - if (!permissionManager) { - return NS_OK; - } - - PRUint32 perm; - permissionManager->TestExactPermission(innerURI, "offline-app", &perm); - - if (perm == nsIPermissionManager::UNKNOWN_ACTION) { - nsCOMPtr branch = aPrefBranch; - if (!branch) { - branch = do_GetService(NS_PREFSERVICE_CONTRACTID); - } - if (branch) { - rv = branch->GetBoolPref("offline-apps.allow_by_default", aAllowed); - if (NS_FAILED(rv)) { - *aAllowed = PR_FALSE; - } - } - - return NS_OK; - } - - if (perm == nsIPermissionManager::DENY_ACTION) { - return NS_OK; - } - - *aAllowed = PR_TRUE; - - return NS_OK; + return AssociateDocuments(applicationCache); } diff --git a/uriloader/prefetch/nsOfflineCacheUpdate.h b/uriloader/prefetch/nsOfflineCacheUpdate.h index 400fcf2ac66..e9e29e4a33e 100644 --- a/uriloader/prefetch/nsOfflineCacheUpdate.h +++ b/uriloader/prefetch/nsOfflineCacheUpdate.h @@ -211,11 +211,13 @@ public: }; class nsOfflineCacheUpdate : public nsIOfflineCacheUpdate + , public nsIOfflineCacheUpdateObserver , public nsOfflineCacheUpdateOwner { public: NS_DECL_ISUPPORTS NS_DECL_NSIOFFLINECACHEUPDATE + NS_DECL_NSIOFFLINECACHEUPDATEOBSERVER nsOfflineCacheUpdate(); ~nsOfflineCacheUpdate(); @@ -230,7 +232,7 @@ public: void LoadCompleted(); void ManifestCheckCompleted(nsresult aStatus, const nsCString &aManifestHash); - void AddDocument(nsIDOMDocument *aDocument); + void StickDocument(nsIURI *aDocumentURI); void SetOwner(nsOfflineCacheUpdateOwner *aOwner); @@ -247,19 +249,13 @@ private: // specified namespaces will be added. nsresult AddExistingItems(PRUint32 aType, nsTArray* namespaceFilter = nsnull); + nsresult ScheduleImplicit(); + nsresult AssociateDocuments(nsIApplicationCache* cache); nsresult GatherObservers(nsCOMArray &aObservers); - nsresult NotifyError(); - nsresult NotifyChecking(); - nsresult NotifyNoUpdate(); - nsresult NotifyObsolete(); - nsresult NotifyDownloading(); - nsresult NotifyStarted(nsOfflineCacheUpdateItem *aItem); - nsresult NotifyCompleted(nsOfflineCacheUpdateItem *aItem); - nsresult AssociateDocument(nsIDOMDocument *aDocument, - nsIApplicationCache *aApplicationCache); - nsresult ScheduleImplicit(); + nsresult NotifyState(PRUint32 state); nsresult Finish(); + nsresult FinishNoNotify(); enum { STATE_UNINITIALIZED, @@ -279,7 +275,6 @@ private: nsCString mUpdateDomain; nsCOMPtr mManifestURI; - nsCOMPtr mDocumentURI; nsCString mClientID; @@ -299,7 +294,7 @@ private: nsCOMArray mObservers; /* Documents that requested this update */ - nsCOMArray mDocuments; + nsCOMArray mDocumentURIs; /* Reschedule count. When an update is rescheduled due to * mismatched manifests, the reschedule count will be increased. */ @@ -323,10 +318,15 @@ public: nsresult Init(); - nsresult Schedule(nsOfflineCacheUpdate *aUpdate); + nsresult ScheduleUpdate(nsOfflineCacheUpdate *aUpdate); + nsresult FindUpdate(nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsOfflineCacheUpdate **aUpdate); + nsresult Schedule(nsIURI *aManifestURI, nsIURI *aDocumentURI, nsIDOMDocument *aDocument, + nsIDOMWindow* aWindow, nsIOfflineCacheUpdate **aUpdate); virtual nsresult UpdateFinished(nsOfflineCacheUpdate *aUpdate); diff --git a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp new file mode 100644 index 00000000000..52d95aa34d2 --- /dev/null +++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp @@ -0,0 +1,612 @@ +/* -*- 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 + * + * 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 ***** */ + +#ifdef MOZ_IPC +#include "OfflineCacheUpdateChild.h" +#include "OfflineCacheUpdateParent.h" +#include "nsXULAppAPI.h" +#endif +#include "OfflineCacheUpdateGlue.h" +#include "nsOfflineCacheUpdate.h" + +#include "nsCPrefetchService.h" +#include "nsCURILoader.h" +#include "nsIApplicationCacheContainer.h" +#include "nsIApplicationCacheChannel.h" +#include "nsIApplicationCacheService.h" +#include "nsICache.h" +#include "nsICacheService.h" +#include "nsICacheSession.h" +#include "nsICachingChannel.h" +#include "nsIContent.h" +#include "nsIDocumentLoader.h" +#include "nsIDOMElement.h" +#include "nsIDOMWindow.h" +#include "nsIDOMOfflineResourceList.h" +#include "nsIDocument.h" +#include "nsIObserverService.h" +#include "nsIURL.h" +#include "nsIWebProgress.h" +#include "nsICryptoHash.h" +#include "nsICacheEntryDescriptor.h" +#include "nsIPermissionManager.h" +#include "nsIPrincipal.h" +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsStreamUtils.h" +#include "nsThreadUtils.h" +#include "nsProxyRelease.h" +#include "prlog.h" +#include "nsIAsyncVerifyRedirectCallback.h" + +static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nsnull; + +#ifdef MOZ_IPC +typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent; +typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild; +#endif +typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue; + +#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 +// +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; +}; + +static nsresult +DropReferenceFromURL(nsIURI * aURI) +{ + nsCOMPtr url = do_QueryInterface(aURI); + if (url) { + nsresult rv = url->SetRef(EmptyCString()); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsOfflineCachePendingUpdate +//----------------------------------------------------------------------------- + +class nsOfflineCachePendingUpdate : public nsIWebProgressListener + , public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIWEBPROGRESSLISTENER + + nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService, + nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsIDOMDocument *aDocument) + : mService(aService) + , mManifestURI(aManifestURI) + , mDocumentURI(aDocumentURI) + { + mDocument = do_GetWeakReference(aDocument); + } + +private: + nsRefPtr mService; + nsCOMPtr mManifestURI; + nsCOMPtr mDocumentURI; + nsCOMPtr mDocument; +}; + +NS_IMPL_ISUPPORTS2(nsOfflineCachePendingUpdate, + nsIWebProgressListener, + nsISupportsWeakReference) + +//----------------------------------------------------------------------------- +// nsOfflineCacheUpdateService::nsIWebProgressListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress, + nsIRequest *aRequest, + PRInt32 curSelfProgress, + PRInt32 maxSelfProgress, + PRInt32 curTotalProgress, + PRInt32 maxTotalProgress) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest *aRequest, + PRUint32 progressStateFlags, + nsresult aStatus) +{ + nsCOMPtr updateDoc = do_QueryReferent(mDocument); + if (!updateDoc) { + // The document that scheduled this update has gone away, + // we don't need to listen anymore. + aWebProgress->RemoveProgressListener(this); + NS_RELEASE_THIS(); + return NS_OK; + } + + if (!(progressStateFlags & STATE_STOP)) { + return NS_OK; + } + + nsCOMPtr window; + aWebProgress->GetDOMWindow(getter_AddRefs(window)); + if (!window) return NS_OK; + + nsCOMPtr progressDoc; + window->GetDocument(getter_AddRefs(progressDoc)); + if (!progressDoc) return NS_OK; + + if (!SameCOMIdentity(progressDoc, updateDoc)) { + return NS_OK; + } + + LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]", + this, progressDoc.get())); + + // Only schedule the update if the document loaded successfully + if (NS_SUCCEEDED(aStatus)) { + nsCOMPtr update; + mService->Schedule(mManifestURI, mDocumentURI, + updateDoc, window, getter_AddRefs(update)); + } + + aWebProgress->RemoveProgressListener(this); + NS_RELEASE_THIS(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI *location) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const PRUnichar* aMessage) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRUint32 state) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsOfflineCacheUpdateService::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS3(nsOfflineCacheUpdateService, + nsIOfflineCacheUpdateService, + nsIObserver, + nsISupportsWeakReference) + +//----------------------------------------------------------------------------- +// nsOfflineCacheUpdateService +//----------------------------------------------------------------------------- + +nsOfflineCacheUpdateService::nsOfflineCacheUpdateService() + : mDisabled(PR_FALSE) + , mUpdateRunning(PR_FALSE) +{ +} + +nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService() +{ + gOfflineCacheUpdateService = nsnull; +} + +nsresult +nsOfflineCacheUpdateService::Init() +{ +#if defined(PR_LOGGING) + if (!gOfflineCacheUpdateLog) + gOfflineCacheUpdateLog = PR_NewLogModule("nsOfflineCacheUpdate"); +#endif + + // Observe xpcom-shutdown event + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (!observerService) + return NS_ERROR_FAILURE; + + nsresult rv = observerService->AddObserver(this, + NS_XPCOM_SHUTDOWN_OBSERVER_ID, + PR_TRUE); + NS_ENSURE_SUCCESS(rv, rv); + + gOfflineCacheUpdateService = this; + + return NS_OK; +} + +/* static */ +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; +} + +/* static */ +nsOfflineCacheUpdateService * +nsOfflineCacheUpdateService::EnsureService() +{ + if (!gOfflineCacheUpdateService) { + // Make the service manager hold a long-lived reference to the service + nsCOMPtr service = + do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID); + } + + return gOfflineCacheUpdateService; +} + +nsresult +nsOfflineCacheUpdateService::ScheduleUpdate(nsOfflineCacheUpdate *aUpdate) +{ + LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]", + this, aUpdate)); + + aUpdate->SetOwner(this); + + mUpdates.AppendElement(aUpdate); + ProcessNextUpdate(); + + return NS_OK; +} + +NS_IMETHODIMP +nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsIDOMDocument *aDocument) +{ + LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]", + this, aManifestURI, aDocumentURI, aDocument)); + + nsCOMPtr doc = do_QueryInterface(aDocument); + nsCOMPtr container = doc->GetContainer(); + nsCOMPtr progress = do_QueryInterface(container); + NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG); + + // Proceed with cache update + nsRefPtr update = + new nsOfflineCachePendingUpdate(this, aManifestURI, + aDocumentURI, aDocument); + NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = progress->AddProgressListener + (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT); + NS_ENSURE_SUCCESS(rv, rv); + + // The update will release when it has scheduled itself. + update.forget(); + + 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 update = mUpdates[0]; + mUpdates.RemoveElementAt(0); + mUpdateRunning = PR_FALSE; + + ProcessNextUpdate(); + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsOfflineCacheUpdateService +//----------------------------------------------------------------------------- + +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; +} + +nsresult +nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsOfflineCacheUpdate **aUpdate) +{ + nsresult rv; + + nsRefPtr update; + for (PRUint32 i = 0; i < mUpdates.Length(); i++) { + update = mUpdates[i]; + + PRBool partial; + rv = update->GetPartial(&partial); + NS_ENSURE_SUCCESS(rv, rv); + + if (partial) { + // Partial updates aren't considered + continue; + } + + nsCOMPtr manifestURI; + update->GetManifestURI(getter_AddRefs(manifestURI)); + if (manifestURI) { + PRBool equals; + rv = manifestURI->Equals(aManifestURI, &equals); + if (equals) { + update.swap(*aUpdate); + return NS_OK; + } + } + } + + return NS_ERROR_NOT_AVAILABLE; +} + +nsresult +nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsIDOMDocument *aDocument, + nsIDOMWindow* aWindow, + nsIOfflineCacheUpdate **aUpdate) +{ + nsCOMPtr update; +#ifdef MOZ_IPC + if (GeckoProcessType_Default != XRE_GetProcessType()) { + update = new OfflineCacheUpdateChild(aWindow); + } + else +#endif + { + update = new OfflineCacheUpdateGlue(); + } + + nsresult rv; + + rv = update->Init(aManifestURI, aDocumentURI, aDocument); + NS_ENSURE_SUCCESS(rv, rv); + + rv = update->Schedule(); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ADDREF(*aUpdate = update); + + return NS_OK; +} + +NS_IMETHODIMP +nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI, + nsIURI *aDocumentURI, + nsIDOMWindow *aWindow, + nsIOfflineCacheUpdate **aUpdate) +{ + return Schedule(aManifestURI, aDocumentURI, nsnull, aWindow, aUpdate); +} + +//----------------------------------------------------------------------------- +// 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::nsIOfflineCacheUpdateService +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal, + nsIPrefBranch *aPrefBranch, + PRBool *aAllowed) +{ + nsCOMPtr codebaseURI; + nsresult rv = aPrincipal->GetURI(getter_AddRefs(codebaseURI)); + NS_ENSURE_SUCCESS(rv, rv); + + return OfflineAppAllowedForURI(codebaseURI, aPrefBranch, aAllowed); +} + +NS_IMETHODIMP +nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI, + nsIPrefBranch *aPrefBranch, + PRBool *aAllowed) +{ + *aAllowed = PR_FALSE; + if (!aURI) + return NS_OK; + + nsCOMPtr innerURI = NS_GetInnermostURI(aURI); + if (!innerURI) + return NS_OK; + + // only http and https applications can use offline APIs. + PRBool match; + 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 permissionManager = + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); + if (!permissionManager) { + return NS_OK; + } + + PRUint32 perm; + permissionManager->TestExactPermission(innerURI, "offline-app", &perm); + + if (perm == nsIPermissionManager::UNKNOWN_ACTION) { + nsCOMPtr branch = aPrefBranch; + if (!branch) { + branch = do_GetService(NS_PREFSERVICE_CONTRACTID); + } + if (branch) { + rv = branch->GetBoolPref("offline-apps.allow_by_default", aAllowed); + if (NS_FAILED(rv)) { + *aAllowed = PR_FALSE; + } + } + + return NS_OK; + } + + if (perm == nsIPermissionManager::DENY_ACTION) { + return NS_OK; + } + + *aAllowed = PR_TRUE; + + return NS_OK; +}