summaryrefslogtreecommitdiffstats
path: root/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'uriloader/prefetch/nsOfflineCacheUpdateService.cpp')
-rw-r--r--uriloader/prefetch/nsOfflineCacheUpdateService.cpp736
1 files changed, 736 insertions, 0 deletions
diff --git a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
new file mode 100644
index 000000000..adb3fd516
--- /dev/null
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -0,0 +1,736 @@
+/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OfflineCacheUpdateChild.h"
+#include "OfflineCacheUpdateParent.h"
+#include "nsXULAppAPI.h"
+#include "OfflineCacheUpdateGlue.h"
+#include "nsOfflineCacheUpdate.h"
+
+#include "nsCPrefetchService.h"
+#include "nsCURILoader.h"
+#include "nsIApplicationCacheContainer.h"
+#include "nsIApplicationCacheChannel.h"
+#include "nsIApplicationCacheService.h"
+#include "nsICachingChannel.h"
+#include "nsIContent.h"
+#include "nsIDocShell.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 "nsIWebNavigation.h"
+#include "nsICryptoHash.h"
+#include "nsIPermissionManager.h"
+#include "nsIPrincipal.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStreamUtils.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+#include "mozilla/Logging.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Unused.h"
+#include "nsIDiskSpaceWatcher.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "nsContentUtils.h"
+#include "mozilla/Unused.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+static nsOfflineCacheUpdateService *gOfflineCacheUpdateService = nullptr;
+static bool sAllowOfflineCache = true;
+
+nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::mAllowedDomains = nullptr;
+
+nsTHashtable<nsCStringHashKey>* nsOfflineCacheUpdateService::AllowedDomains()
+{
+ if (!mAllowedDomains)
+ mAllowedDomains = new nsTHashtable<nsCStringHashKey>();
+
+ return mAllowedDomains;
+}
+
+
+typedef mozilla::docshell::OfflineCacheUpdateParent OfflineCacheUpdateParent;
+typedef mozilla::docshell::OfflineCacheUpdateChild OfflineCacheUpdateChild;
+typedef mozilla::docshell::OfflineCacheUpdateGlue OfflineCacheUpdateGlue;
+
+//
+// To enable logging (see mozilla/Logging.h for full details):
+//
+// set MOZ_LOG=nsOfflineCacheUpdate:5
+// set MOZ_LOG_FILE=offlineupdate.log
+//
+// this enables LogLevel::Debug level information and places all output in
+// the file offlineupdate.log
+//
+LazyLogModule gOfflineCacheUpdateLog("nsOfflineCacheUpdate");
+
+#undef LOG
+#define LOG(args) MOZ_LOG(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug, args)
+
+#undef LOG_ENABLED
+#define LOG_ENABLED() MOZ_LOG_TEST(gOfflineCacheUpdateLog, mozilla::LogLevel::Debug)
+
+//-----------------------------------------------------------------------------
+// nsOfflineCachePendingUpdate
+//-----------------------------------------------------------------------------
+
+class nsOfflineCachePendingUpdate final : public nsIWebProgressListener
+ , public nsSupportsWeakReference
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+ nsOfflineCachePendingUpdate(nsOfflineCacheUpdateService *aService,
+ nsIURI *aManifestURI,
+ nsIURI *aDocumentURI,
+ nsIPrincipal* aLoadingPrincipal,
+ nsIDOMDocument *aDocument)
+ : mService(aService)
+ , mManifestURI(aManifestURI)
+ , mDocumentURI(aDocumentURI)
+ , mLoadingPrincipal(aLoadingPrincipal)
+ , mDidReleaseThis(false)
+ {
+ mDocument = do_GetWeakReference(aDocument);
+ }
+
+private:
+ ~nsOfflineCachePendingUpdate() {}
+
+ RefPtr<nsOfflineCacheUpdateService> mService;
+ nsCOMPtr<nsIURI> mManifestURI;
+ nsCOMPtr<nsIURI> mDocumentURI;
+ nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
+ nsCOMPtr<nsIWeakReference> mDocument;
+ bool mDidReleaseThis;
+};
+
+NS_IMPL_ISUPPORTS(nsOfflineCachePendingUpdate,
+ nsIWebProgressListener,
+ nsISupportsWeakReference)
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsIWebProgressListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnProgressChange(nsIWebProgress *aProgress,
+ nsIRequest *aRequest,
+ int32_t curSelfProgress,
+ int32_t maxSelfProgress,
+ int32_t curTotalProgress,
+ int32_t maxTotalProgress)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnStateChange(nsIWebProgress* aWebProgress,
+ nsIRequest *aRequest,
+ uint32_t progressStateFlags,
+ nsresult aStatus)
+{
+ if (mDidReleaseThis) {
+ return NS_OK;
+ }
+ nsCOMPtr<nsIDOMDocument> 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);
+ MOZ_ASSERT(!mDidReleaseThis);
+ mDidReleaseThis = true;
+ NS_RELEASE_THIS();
+ return NS_OK;
+ }
+
+ if (!(progressStateFlags & STATE_STOP)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> windowProxy;
+ aWebProgress->GetDOMWindow(getter_AddRefs(windowProxy));
+ if (!windowProxy) return NS_OK;
+
+ auto* outerWindow = nsPIDOMWindowOuter::From(windowProxy);
+ nsPIDOMWindowInner* innerWindow = outerWindow->GetCurrentInnerWindow();
+
+ nsCOMPtr<nsIDocument> progressDoc = outerWindow->GetDoc();
+ 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<nsIOfflineCacheUpdate> update;
+ mService->Schedule(mManifestURI, mDocumentURI, mLoadingPrincipal, updateDoc, innerWindow,
+ nullptr, getter_AddRefs(update));
+ if (mDidReleaseThis) {
+ return NS_OK;
+ }
+ }
+
+ aWebProgress->RemoveProgressListener(this);
+ MOZ_ASSERT(!mDidReleaseThis);
+ mDidReleaseThis = true;
+ NS_RELEASE_THIS();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI *location,
+ uint32_t aFlags)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ uint32_t state)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateService,
+ nsIOfflineCacheUpdateService,
+ nsIObserver,
+ nsISupportsWeakReference)
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService <public>
+//-----------------------------------------------------------------------------
+
+nsOfflineCacheUpdateService::nsOfflineCacheUpdateService()
+ : mDisabled(false)
+ , mUpdateRunning(false)
+ , mLowFreeSpace(false)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ Preferences::AddBoolVarCache(&sAllowOfflineCache,
+ "browser.cache.offline.enable",
+ true);
+}
+
+nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService()
+{
+ gOfflineCacheUpdateService = nullptr;
+}
+
+nsresult
+nsOfflineCacheUpdateService::Init()
+{
+ // Observe xpcom-shutdown event
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = observerService->AddObserver(this,
+ NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the current status of the disk in terms of free space and observe
+ // low device storage notifications.
+ nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcherService =
+ do_GetService("@mozilla.org/toolkit/disk-space-watcher;1");
+ if (diskSpaceWatcherService) {
+ diskSpaceWatcherService->GetIsDiskFull(&mLowFreeSpace);
+ } else {
+ NS_WARNING("Could not get disk status from nsIDiskSpaceWatcher");
+ }
+
+ rv = observerService->AddObserver(this, "disk-space-watcher", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ gOfflineCacheUpdateService = this;
+
+ return NS_OK;
+}
+
+/* static */
+nsOfflineCacheUpdateService *
+nsOfflineCacheUpdateService::GetInstance()
+{
+ if (!gOfflineCacheUpdateService) {
+ gOfflineCacheUpdateService = new nsOfflineCacheUpdateService();
+ if (!gOfflineCacheUpdateService)
+ return nullptr;
+ NS_ADDREF(gOfflineCacheUpdateService);
+ nsresult rv = gOfflineCacheUpdateService->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(gOfflineCacheUpdateService);
+ return nullptr;
+ }
+ 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<nsIOfflineCacheUpdateService> 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,
+ nsIPrincipal* aLoadingPrincipal,
+ nsIDOMDocument *aDocument)
+{
+ LOG(("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, manifestURI=%p, documentURI=%p doc=%p]",
+ this, aManifestURI, aDocumentURI, aDocument));
+
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
+ nsCOMPtr<nsIWebProgress> progress = do_QueryInterface(doc->GetContainer());
+ NS_ENSURE_TRUE(progress, NS_ERROR_INVALID_ARG);
+
+ // Proceed with cache update
+ RefPtr<nsOfflineCachePendingUpdate> update =
+ new nsOfflineCachePendingUpdate(this, aManifestURI, aDocumentURI,
+ aLoadingPrincipal, 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.
+ Unused << 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
+ RefPtr<nsOfflineCacheUpdate> update = mUpdates[0];
+ Unused << update;
+ mUpdates.RemoveElementAt(0);
+ mUpdateRunning = false;
+
+ ProcessNextUpdate();
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService <private>
+//-----------------------------------------------------------------------------
+
+nsresult
+nsOfflineCacheUpdateService::ProcessNextUpdate()
+{
+ LOG(("nsOfflineCacheUpdateService::ProcessNextUpdate [%p, num=%d]",
+ this, mUpdates.Length()));
+
+ if (mDisabled)
+ return NS_ERROR_ABORT;
+
+ if (mUpdateRunning)
+ return NS_OK;
+
+ if (mUpdates.Length() > 0) {
+ mUpdateRunning = true;
+ // Canceling the update before Begin() call will make the update
+ // asynchronously finish with an error.
+ if (mLowFreeSpace) {
+ mUpdates[0]->Cancel();
+ }
+ return mUpdates[0]->Begin();
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::GetNumUpdates(uint32_t *aNumUpdates)
+{
+ LOG(("nsOfflineCacheUpdateService::GetNumUpdates [%p]", this));
+
+ *aNumUpdates = mUpdates.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::GetUpdate(uint32_t aIndex,
+ nsIOfflineCacheUpdate **aUpdate)
+{
+ LOG(("nsOfflineCacheUpdateService::GetUpdate [%p, %d]", this, aIndex));
+
+ if (aIndex < mUpdates.Length()) {
+ NS_ADDREF(*aUpdate = mUpdates[aIndex]);
+ } else {
+ *aUpdate = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsOfflineCacheUpdateService::FindUpdate(nsIURI *aManifestURI,
+ nsACString const &aOriginSuffix,
+ nsIFile *aCustomProfileDir,
+ nsOfflineCacheUpdate **aUpdate)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIApplicationCacheService> cacheService =
+ do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString groupID;
+ rv = cacheService->BuildGroupIDForSuffix(aManifestURI, aOriginSuffix, groupID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsOfflineCacheUpdate> update;
+ for (uint32_t i = 0; i < mUpdates.Length(); i++) {
+ update = mUpdates[i];
+
+ bool partial;
+ rv = update->GetPartial(&partial);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (partial) {
+ // Partial updates aren't considered
+ continue;
+ }
+
+ if (update->IsForGroupID(groupID) && update->IsForProfile(aCustomProfileDir)) {
+ update.swap(*aUpdate);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult
+nsOfflineCacheUpdateService::Schedule(nsIURI *aManifestURI,
+ nsIURI *aDocumentURI,
+ nsIPrincipal* aLoadingPrincipal,
+ nsIDOMDocument *aDocument,
+ nsPIDOMWindowInner* aWindow,
+ nsIFile* aCustomProfileDir,
+ nsIOfflineCacheUpdate **aUpdate)
+{
+ nsCOMPtr<nsIOfflineCacheUpdate> update;
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ update = new OfflineCacheUpdateChild(aWindow);
+ }
+ else {
+ update = new OfflineCacheUpdateGlue();
+ }
+
+ nsresult rv;
+
+ if (aWindow) {
+ // Ensure there is window.applicationCache object that is
+ // responsible for association of the new applicationCache
+ // with the corresponding document. Just ignore the result.
+ nsCOMPtr<nsIDOMOfflineResourceList> appCacheWindowObject =
+ aWindow->GetApplicationCache();
+ }
+
+ rv = update->Init(aManifestURI, aDocumentURI, aLoadingPrincipal, aDocument,
+ aCustomProfileDir);
+ 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,
+ nsIPrincipal* aLoadingPrincipal,
+ mozIDOMWindow* aWindow,
+ nsIOfflineCacheUpdate **aUpdate)
+{
+ return Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, nullptr,
+ nsPIDOMWindowInner::From(aWindow), nullptr, aUpdate);
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::ScheduleAppUpdate(nsIURI *aManifestURI,
+ nsIURI *aDocumentURI,
+ nsIPrincipal* aLoadingPrincipal,
+ nsIFile *aProfileDir,
+ nsIOfflineCacheUpdate **aUpdate)
+{
+ return Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, nullptr, nullptr,
+ aProfileDir, aUpdate);
+}
+
+NS_IMETHODIMP nsOfflineCacheUpdateService::CheckForUpdate(nsIURI *aManifestURI,
+ nsIPrincipal* aLoadingPrincipal,
+ nsIObserver *aObserver)
+{
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ // Not intended to support this on child processes
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ nsCOMPtr<nsIOfflineCacheUpdate> update = new OfflineCacheUpdateGlue();
+
+ nsresult rv;
+
+ rv = update->InitForUpdateCheck(aManifestURI, aLoadingPrincipal, aObserver);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = update->Schedule();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsIObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ if (mUpdates.Length() > 0)
+ mUpdates[0]->Cancel();
+ mDisabled = true;
+ }
+
+ if (!strcmp(aTopic, "disk-space-watcher")) {
+ if (NS_LITERAL_STRING("full").Equals(aData)) {
+ mLowFreeSpace = true;
+ for (uint32_t i = 0; i < mUpdates.Length(); i++) {
+ mUpdates[i]->Cancel();
+ }
+ } else if (NS_LITERAL_STRING("free").Equals(aData)) {
+ mLowFreeSpace = false;
+ }
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
+//-----------------------------------------------------------------------------
+
+static nsresult
+OfflineAppPermForPrincipal(nsIPrincipal *aPrincipal,
+ nsIPrefBranch *aPrefBranch,
+ bool pinned,
+ bool *aAllowed)
+{
+ *aAllowed = false;
+
+ if (!sAllowOfflineCache) {
+ return NS_OK;
+ }
+
+ if (!aPrincipal)
+ return NS_ERROR_INVALID_ARG;
+
+ nsCOMPtr<nsIURI> uri;
+ aPrincipal->GetURI(getter_AddRefs(uri));
+
+ if (!uri)
+ return NS_OK;
+
+ nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
+ if (!innerURI)
+ return NS_OK;
+
+ // only http and https applications can use offline APIs.
+ bool 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;
+ }
+ }
+
+ nsAutoCString domain;
+ rv = innerURI->GetAsciiHost(domain);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (nsOfflineCacheUpdateService::AllowedDomains()->Contains(domain)) {
+ *aAllowed = true;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPermissionManager> permissionManager =
+ services::GetPermissionManager();
+ if (!permissionManager) {
+ return NS_OK;
+ }
+
+ uint32_t perm;
+ const char *permName = pinned ? "pin-app" : "offline-app";
+ permissionManager->TestExactPermissionFromPrincipal(aPrincipal, permName, &perm);
+
+ if (perm == nsIPermissionManager::ALLOW_ACTION ||
+ perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) {
+ *aAllowed = true;
+ }
+
+ // offline-apps.allow_by_default is now effective at the cache selection
+ // algorithm code (nsContentSink).
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal *aPrincipal,
+ nsIPrefBranch *aPrefBranch,
+ bool *aAllowed)
+{
+ return OfflineAppPermForPrincipal(aPrincipal, aPrefBranch, false, aAllowed);
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI,
+ nsIPrefBranch *aPrefBranch,
+ bool *aAllowed)
+{
+ PrincipalOriginAttributes attrs;
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
+ return OfflineAppPermForPrincipal(principal, aPrefBranch, false, aAllowed);
+}
+
+nsresult
+nsOfflineCacheUpdateService::OfflineAppPinnedForURI(nsIURI *aDocumentURI,
+ nsIPrefBranch *aPrefBranch,
+ bool *aPinned)
+{
+ PrincipalOriginAttributes attrs;
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateCodebasePrincipal(aDocumentURI, attrs);
+ return OfflineAppPermForPrincipal(principal, aPrefBranch, true, aPinned);
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::AllowOfflineApp(nsIPrincipal *aPrincipal)
+{
+ nsresult rv;
+
+ if (!sAllowOfflineCache) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ ContentChild* child = ContentChild::GetSingleton();
+
+ if (!child->SendSetOfflinePermission(IPC::Principal(aPrincipal))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString domain;
+ rv = aPrincipal->GetBaseDomain(domain);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsOfflineCacheUpdateService::AllowedDomains()->PutEntry(domain);
+ }
+ else {
+ nsCOMPtr<nsIPermissionManager> permissionManager =
+ services::GetPermissionManager();
+ if (!permissionManager)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ rv = permissionManager->AddFromPrincipal(
+ aPrincipal, "offline-app", nsIPermissionManager::ALLOW_ACTION,
+ nsIPermissionManager::EXPIRE_NEVER, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}