michael@0: /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #if defined(MOZ_LOGGING) michael@0: #define FORCE_PR_LOG michael@0: #endif michael@0: michael@0: #include "OfflineCacheUpdateChild.h" michael@0: #include "OfflineCacheUpdateParent.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "OfflineCacheUpdateGlue.h" michael@0: #include "nsOfflineCacheUpdate.h" michael@0: michael@0: #include "nsCPrefetchService.h" michael@0: #include "nsCURILoader.h" michael@0: #include "nsIApplicationCacheContainer.h" michael@0: #include "nsIApplicationCacheChannel.h" michael@0: #include "nsIApplicationCacheService.h" michael@0: #include "nsICache.h" michael@0: #include "nsICacheService.h" michael@0: #include "nsICacheSession.h" michael@0: #include "nsICachingChannel.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIDocumentLoader.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIDOMOfflineResourceList.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIURL.h" michael@0: #include "nsIWebProgress.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsICryptoHash.h" michael@0: #include "nsICacheEntryDescriptor.h" michael@0: #include "nsIPermissionManager.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "prlog.h" michael@0: #include "nsIAsyncVerifyRedirectCallback.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/unused.h" michael@0: #include "nsIDiskSpaceWatcher.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIDocShellTreeItem.h" michael@0: #include "nsIDocShellTreeOwner.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/dom/PermissionMessageUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr; michael@0: michael@0: nsTHashtable* nsOfflineCacheUpdateService::mAllowedDomains = nullptr; michael@0: michael@0: nsTHashtable* nsOfflineCacheUpdateService::AllowedDomains() michael@0: { michael@0: if (!mAllowedDomains) michael@0: mAllowedDomains = new nsTHashtable(); michael@0: michael@0: return mAllowedDomains; michael@0: } michael@0: michael@0: michael@0: typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent; michael@0: typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild; michael@0: typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue; michael@0: michael@0: #if defined(PR_LOGGING) michael@0: // michael@0: // To enable logging (see prlog.h for full details): michael@0: // michael@0: // set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5 michael@0: // set NSPR_LOG_FILE=offlineupdate.log michael@0: // michael@0: // this enables PR_LOG_ALWAYS level information and places all output in michael@0: // the file offlineupdate.log michael@0: // michael@0: PRLogModuleInfo *gOfflineCacheUpdateLog; michael@0: #endif michael@0: michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(gOfflineCacheUpdateLog, 4, args) michael@0: michael@0: #undef LOG_ENABLED michael@0: #define LOG_ENABLED() PR_LOG_TEST(gOfflineCacheUpdateLog, 4) michael@0: michael@0: namespace { // anon michael@0: michael@0: nsresult michael@0: GetAppIDAndInBrowserFromWindow(nsIDOMWindow *aWindow, michael@0: uint32_t *aAppId, michael@0: bool *aInBrowser) michael@0: { michael@0: *aAppId = NECKO_NO_APP_ID; michael@0: *aInBrowser = false; michael@0: michael@0: if (!aWindow) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr loadContext = do_GetInterface(aWindow); michael@0: if (!loadContext) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: rv = loadContext->GetAppId(aAppId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = loadContext->GetIsInBrowserElement(aInBrowser); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anon michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsOfflineCachePendingUpdate michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: class nsOfflineCachePendingUpdate MOZ_FINAL : public nsIWebProgressListener michael@0: , public nsSupportsWeakReference michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIWEBPROGRESSLISTENER michael@0: michael@0: nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService, michael@0: nsIURI *aManifestURI, michael@0: nsIURI *aDocumentURI, michael@0: nsIDOMDocument *aDocument) michael@0: : mService(aService) michael@0: , mManifestURI(aManifestURI) michael@0: , mDocumentURI(aDocumentURI) michael@0: , mDidReleaseThis(false) michael@0: { michael@0: mDocument = do_GetWeakReference(aDocument); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mService; michael@0: nsCOMPtr mManifestURI; michael@0: nsCOMPtr mDocumentURI; michael@0: nsCOMPtr mDocument; michael@0: bool mDidReleaseThis; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsOfflineCachePendingUpdate, michael@0: nsIWebProgressListener, michael@0: nsISupportsWeakReference) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsOfflineCacheUpdateService::nsIWebProgressListener michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress, michael@0: nsIRequest *aRequest, michael@0: int32_t curSelfProgress, michael@0: int32_t maxSelfProgress, michael@0: int32_t curTotalProgress, michael@0: int32_t maxTotalProgress) michael@0: { michael@0: NS_NOTREACHED("notification excluded in AddProgressListener(...)"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress, michael@0: nsIRequest *aRequest, michael@0: uint32_t progressStateFlags, michael@0: nsresult aStatus) michael@0: { michael@0: if (mDidReleaseThis) { michael@0: return NS_OK; michael@0: } michael@0: nsCOMPtr updateDoc = do_QueryReferent(mDocument); michael@0: if (!updateDoc) { michael@0: // The document that scheduled this update has gone away, michael@0: // we don't need to listen anymore. michael@0: aWebProgress->RemoveProgressListener(this); michael@0: MOZ_ASSERT(!mDidReleaseThis); michael@0: mDidReleaseThis = true; michael@0: NS_RELEASE_THIS(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!(progressStateFlags & STATE_STOP)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr window; michael@0: aWebProgress->GetDOMWindow(getter_AddRefs(window)); michael@0: if (!window) return NS_OK; michael@0: michael@0: nsCOMPtr progressDoc; michael@0: window->GetDocument(getter_AddRefs(progressDoc)); michael@0: if (!progressDoc) return NS_OK; michael@0: michael@0: if (!SameCOMIdentity(progressDoc, updateDoc)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: LOG(("nsOfflineCachePendingUpdate::OnStateChange [%p, doc=%p]", michael@0: this, progressDoc.get())); michael@0: michael@0: // Only schedule the update if the document loaded successfully michael@0: if (NS_SUCCEEDED(aStatus)) { michael@0: // Get extended origin attributes michael@0: uint32_t appId; michael@0: bool isInBrowserElement; michael@0: nsresult rv = GetAppIDAndInBrowserFromWindow(window, &appId, &isInBrowserElement); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr update; michael@0: mService->Schedule(mManifestURI, mDocumentURI, michael@0: updateDoc, window, nullptr, michael@0: appId, isInBrowserElement, getter_AddRefs(update)); michael@0: if (mDidReleaseThis) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: aWebProgress->RemoveProgressListener(this); michael@0: MOZ_ASSERT(!mDidReleaseThis); michael@0: mDidReleaseThis = true; michael@0: NS_RELEASE_THIS(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress, michael@0: nsIRequest* aRequest, michael@0: nsIURI *location, michael@0: uint32_t aFlags) michael@0: { michael@0: NS_NOTREACHED("notification excluded in AddProgressListener(...)"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress, michael@0: nsIRequest* aRequest, michael@0: nsresult aStatus, michael@0: const char16_t* aMessage) michael@0: { michael@0: NS_NOTREACHED("notification excluded in AddProgressListener(...)"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress, michael@0: nsIRequest *aRequest, michael@0: uint32_t state) michael@0: { michael@0: NS_NOTREACHED("notification excluded in AddProgressListener(...)"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsOfflineCacheUpdateService::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateService, michael@0: nsIOfflineCacheUpdateService, michael@0: nsIObserver, michael@0: nsISupportsWeakReference) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsOfflineCacheUpdateService michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsOfflineCacheUpdateService::nsOfflineCacheUpdateService() michael@0: : mDisabled(false) michael@0: , mUpdateRunning(false) michael@0: , mLowFreeSpace(false) michael@0: { michael@0: } michael@0: michael@0: nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService() michael@0: { michael@0: gOfflineCacheUpdateService = nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsOfflineCacheUpdateService::Init() michael@0: { michael@0: #if defined(PR_LOGGING) michael@0: if (!gOfflineCacheUpdateLog) michael@0: gOfflineCacheUpdateLog = PR_NewLogModule("nsOfflineCacheUpdate"); michael@0: #endif michael@0: michael@0: // Observe xpcom-shutdown event michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (!observerService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv = observerService->AddObserver(this, michael@0: NS_XPCOM_SHUTDOWN_OBSERVER_ID, michael@0: true); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Get the current status of the disk in terms of free space and observe michael@0: // low device storage notifications. michael@0: nsCOMPtr diskSpaceWatcherService = michael@0: do_GetService("@mozilla.org/toolkit/disk-space-watcher;1"); michael@0: if (diskSpaceWatcherService) { michael@0: diskSpaceWatcherService->GetIsDiskFull(&mLowFreeSpace); michael@0: } else { michael@0: NS_WARNING("Could not get disk status from nsIDiskSpaceWatcher"); michael@0: } michael@0: michael@0: rv = observerService->AddObserver(this, "disk-space-watcher", false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: gOfflineCacheUpdateService = this; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: nsOfflineCacheUpdateService * michael@0: nsOfflineCacheUpdateService::GetInstance() michael@0: { michael@0: if (!gOfflineCacheUpdateService) { michael@0: gOfflineCacheUpdateService = new nsOfflineCacheUpdateService(); michael@0: if (!gOfflineCacheUpdateService) michael@0: return nullptr; michael@0: NS_ADDREF(gOfflineCacheUpdateService); michael@0: nsresult rv = gOfflineCacheUpdateService->Init(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_RELEASE(gOfflineCacheUpdateService); michael@0: return nullptr; michael@0: } michael@0: return gOfflineCacheUpdateService; michael@0: } michael@0: michael@0: NS_ADDREF(gOfflineCacheUpdateService); michael@0: michael@0: return gOfflineCacheUpdateService; michael@0: } michael@0: michael@0: /* static */ michael@0: nsOfflineCacheUpdateService * michael@0: nsOfflineCacheUpdateService::EnsureService() michael@0: { michael@0: if (!gOfflineCacheUpdateService) { michael@0: // Make the service manager hold a long-lived reference to the service michael@0: nsCOMPtr service = michael@0: do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID); michael@0: } michael@0: michael@0: return gOfflineCacheUpdateService; michael@0: } michael@0: michael@0: nsresult michael@0: nsOfflineCacheUpdateService::ScheduleUpdate(nsOfflineCacheUpdate *aUpdate) michael@0: { michael@0: LOG(("nsOfflineCacheUpdateService::Schedule [%p, update=%p]", michael@0: this, aUpdate)); michael@0: michael@0: aUpdate->SetOwner(this); michael@0: michael@0: mUpdates.AppendElement(aUpdate); michael@0: ProcessNextUpdate(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::ScheduleOnDocumentStop(nsIURI *aManifestURI, michael@0: nsIURI *aDocumentURI, michael@0: nsIDOMDocument *aDocument) michael@0: { michael@0: LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]", michael@0: this, aManifestURI, aDocumentURI, aDocument)); michael@0: michael@0: nsCOMPtr doc = do_QueryInterface(aDocument); michael@0: nsCOMPtr progress = do_QueryInterface(doc->GetContainer()); michael@0: NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG); michael@0: michael@0: // Proceed with cache update michael@0: nsRefPtr update = michael@0: new nsOfflineCachePendingUpdate(this, aManifestURI, michael@0: aDocumentURI, aDocument); michael@0: NS_ENSURE_TRUE(update, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nsresult rv = progress->AddProgressListener michael@0: (update, nsIWebProgress::NOTIFY_STATE_DOCUMENT); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // The update will release when it has scheduled itself. michael@0: unused << update.forget(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsOfflineCacheUpdateService::UpdateFinished(nsOfflineCacheUpdate *aUpdate) michael@0: { michael@0: LOG(("nsOfflineCacheUpdateService::UpdateFinished [%p, update=%p]", michael@0: this, aUpdate)); michael@0: michael@0: NS_ASSERTION(mUpdates.Length() > 0 && michael@0: mUpdates[0] == aUpdate, "Unknown update completed"); michael@0: michael@0: // keep this item alive until we're done notifying observers michael@0: nsRefPtr update = mUpdates[0]; michael@0: mUpdates.RemoveElementAt(0); michael@0: mUpdateRunning = false; michael@0: michael@0: ProcessNextUpdate(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsOfflineCacheUpdateService michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: nsOfflineCacheUpdateService::ProcessNextUpdate() michael@0: { michael@0: LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%d]", michael@0: this, mUpdates.Length())); michael@0: michael@0: if (mDisabled) michael@0: return NS_ERROR_ABORT; michael@0: michael@0: if (mUpdateRunning) michael@0: return NS_OK; michael@0: michael@0: if (mUpdates.Length() > 0) { michael@0: mUpdateRunning = true; michael@0: // Canceling the update before Begin() call will make the update michael@0: // asynchronously finish with an error. michael@0: if (mLowFreeSpace) { michael@0: mUpdates[0]->Cancel(); michael@0: } michael@0: return mUpdates[0]->Begin(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::GetNumUpdates(uint32_t *aNumUpdates) michael@0: { michael@0: LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this)); michael@0: michael@0: *aNumUpdates = mUpdates.Length(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::GetUpdate(uint32_t aIndex, michael@0: nsIOfflineCacheUpdate **aUpdate) michael@0: { michael@0: LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex)); michael@0: michael@0: if (aIndex < mUpdates.Length()) { michael@0: NS_ADDREF(*aUpdate = mUpdates[aIndex]); michael@0: } else { michael@0: *aUpdate = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI, michael@0: uint32_t aAppID, michael@0: bool aInBrowser, michael@0: nsOfflineCacheUpdate **aUpdate) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr cacheService = michael@0: do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString groupID; michael@0: rv = cacheService->BuildGroupIDForApp(aManifestURI, michael@0: aAppID, aInBrowser, michael@0: groupID); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr update; michael@0: for (uint32_t i = 0; i < mUpdates.Length(); i++) { michael@0: update = mUpdates[i]; michael@0: michael@0: bool partial; michael@0: rv = update->GetPartial(&partial); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (partial) { michael@0: // Partial updates aren't considered michael@0: continue; michael@0: } michael@0: michael@0: if (update->IsForGroupID(groupID)) { michael@0: update.swap(*aUpdate); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsresult michael@0: nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI, michael@0: nsIURI *aDocumentURI, michael@0: nsIDOMDocument *aDocument, michael@0: nsIDOMWindow* aWindow, michael@0: nsIFile* aCustomProfileDir, michael@0: uint32_t aAppID, michael@0: bool aInBrowser, michael@0: nsIOfflineCacheUpdate **aUpdate) michael@0: { michael@0: nsCOMPtr update; michael@0: if (GeckoProcessType_Default != XRE_GetProcessType()) { michael@0: update = new OfflineCacheUpdateChild(aWindow); michael@0: } michael@0: else { michael@0: update = new OfflineCacheUpdateGlue(); michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: if (aWindow) { michael@0: // Ensure there is window.applicationCache object that is michael@0: // responsible for association of the new applicationCache michael@0: // with the corresponding document. Just ignore the result. michael@0: nsCOMPtr appCacheWindowObject; michael@0: aWindow->GetApplicationCache(getter_AddRefs(appCacheWindowObject)); michael@0: } michael@0: michael@0: rv = update->Init(aManifestURI, aDocumentURI, aDocument, michael@0: aCustomProfileDir, aAppID, aInBrowser); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = update->Schedule(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ADDREF(*aUpdate = update); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::ScheduleUpdate(nsIURI *aManifestURI, michael@0: nsIURI *aDocumentURI, michael@0: nsIDOMWindow *aWindow, michael@0: nsIOfflineCacheUpdate **aUpdate) michael@0: { michael@0: // Get extended origin attributes michael@0: uint32_t appId; michael@0: bool isInBrowser; michael@0: nsresult rv = GetAppIDAndInBrowserFromWindow(aWindow, &appId, &isInBrowser); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return Schedule(aManifestURI, aDocumentURI, nullptr, aWindow, nullptr, michael@0: appId, isInBrowser, aUpdate); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::ScheduleAppUpdate(nsIURI *aManifestURI, michael@0: nsIURI *aDocumentURI, michael@0: uint32_t aAppID, bool aInBrowser, michael@0: nsIFile *aProfileDir, michael@0: nsIOfflineCacheUpdate **aUpdate) michael@0: { michael@0: return Schedule(aManifestURI, aDocumentURI, nullptr, nullptr, aProfileDir, michael@0: aAppID, aInBrowser, aUpdate); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsOfflineCacheUpdateService::CheckForUpdate(nsIURI *aManifestURI, michael@0: uint32_t aAppID, michael@0: bool aInBrowser, michael@0: nsIObserver *aObserver) michael@0: { michael@0: if (GeckoProcessType_Default != XRE_GetProcessType()) { michael@0: // Not intended to support this on child processes michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsCOMPtr update = new OfflineCacheUpdateGlue(); michael@0: michael@0: nsresult rv; michael@0: michael@0: rv = update->InitForUpdateCheck(aManifestURI, aAppID, aInBrowser, aObserver); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = update->Schedule(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsOfflineCacheUpdateService::nsIObserver michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { michael@0: if (mUpdates.Length() > 0) michael@0: mUpdates[0]->Cancel(); michael@0: mDisabled = true; michael@0: } michael@0: michael@0: if (!strcmp(aTopic, "disk-space-watcher")) { michael@0: if (NS_LITERAL_STRING("full").Equals(aData)) { michael@0: mLowFreeSpace = true; michael@0: for (uint32_t i = 0; i < mUpdates.Length(); i++) { michael@0: mUpdates[i]->Cancel(); michael@0: } michael@0: } else if (NS_LITERAL_STRING("free").Equals(aData)) { michael@0: mLowFreeSpace = false; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: static nsresult michael@0: OfflineAppPermForPrincipal(nsIPrincipal *aPrincipal, michael@0: nsIPrefBranch *aPrefBranch, michael@0: bool pinned, michael@0: bool *aAllowed) michael@0: { michael@0: *aAllowed = false; michael@0: michael@0: if (!aPrincipal) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr uri; michael@0: aPrincipal->GetURI(getter_AddRefs(uri)); michael@0: michael@0: if (!uri) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr innerURI = NS_GetInnermostURI(uri); michael@0: if (!innerURI) michael@0: return NS_OK; michael@0: michael@0: // only http and https applications can use offline APIs. michael@0: bool match; michael@0: nsresult rv = innerURI->SchemeIs("http", &match); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (!match) { michael@0: rv = innerURI->SchemeIs("https", &match); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!match) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: nsAutoCString domain; michael@0: rv = innerURI->GetAsciiHost(domain); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (nsOfflineCacheUpdateService::AllowedDomains()->Contains(domain)) { michael@0: *aAllowed = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr permissionManager = michael@0: do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); michael@0: if (!permissionManager) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t perm; michael@0: const char *permName = pinned ? "pin-app" : "offline-app"; michael@0: permissionManager->TestExactPermissionFromPrincipal(aPrincipal, permName, &perm); michael@0: michael@0: if (perm == nsIPermissionManager::ALLOW_ACTION || michael@0: perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) { michael@0: *aAllowed = true; michael@0: } michael@0: michael@0: // offline-apps.allow_by_default is now effective at the cache selection michael@0: // algorithm code (nsContentSink). michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal, michael@0: nsIPrefBranch *aPrefBranch, michael@0: bool *aAllowed) michael@0: { michael@0: return OfflineAppPermForPrincipal(aPrincipal, aPrefBranch, false, aAllowed); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI, michael@0: nsIPrefBranch *aPrefBranch, michael@0: bool *aAllowed) michael@0: { michael@0: nsCOMPtr principal; michael@0: nsContentUtils::GetSecurityManager()-> michael@0: GetNoAppCodebasePrincipal(aURI, getter_AddRefs(principal)); michael@0: return OfflineAppPermForPrincipal(principal, aPrefBranch, false, aAllowed); michael@0: } michael@0: michael@0: nsresult michael@0: nsOfflineCacheUpdateService::OfflineAppPinnedForURI(nsIURI *aDocumentURI, michael@0: nsIPrefBranch *aPrefBranch, michael@0: bool *aPinned) michael@0: { michael@0: nsCOMPtr principal; michael@0: nsContentUtils::GetSecurityManager()-> michael@0: GetNoAppCodebasePrincipal(aDocumentURI, getter_AddRefs(principal)); michael@0: return OfflineAppPermForPrincipal(principal, aPrefBranch, true, aPinned); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOfflineCacheUpdateService::AllowOfflineApp(nsIDOMWindow *aWindow, michael@0: nsIPrincipal *aPrincipal) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (GeckoProcessType_Default != XRE_GetProcessType()) { michael@0: TabChild* child = TabChild::GetFrom(aWindow); michael@0: NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); michael@0: michael@0: if (!child->SendSetOfflinePermission(IPC::Principal(aPrincipal))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsAutoCString domain; michael@0: rv = aPrincipal->GetBaseDomain(domain); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsOfflineCacheUpdateService::AllowedDomains()->PutEntry(domain); michael@0: } michael@0: else { michael@0: nsCOMPtr permissionManager = michael@0: do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); michael@0: if (!permissionManager) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: rv = permissionManager->AddFromPrincipal( michael@0: aPrincipal, "offline-app", nsIPermissionManager::ALLOW_ACTION, michael@0: nsIPermissionManager::EXPIRE_NEVER, 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: }