summaryrefslogtreecommitdiffstats
path: root/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--uriloader/prefetch/nsOfflineCacheUpdateService.cpp646
1 files changed, 646 insertions, 0 deletions
diff --git a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
new file mode 100644
index 0000000000..3f43193e39
--- /dev/null
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -0,0 +1,646 @@
+/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsCURILoader.h"
+#include "nsIApplicationCacheService.h"
+#include "nsIContent.h"
+#include "mozilla/dom/Document.h"
+#include "nsIObserverService.h"
+#include "nsIWebProgress.h"
+#include "nsIPermissionManager.h"
+#include "nsIPrincipal.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStreamUtils.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Components.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/Unused.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;
+
+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,
+ Document* 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;
+ nsWeakPtr 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) {
+ MOZ_ASSERT_UNREACHABLE("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<Document> 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<Document> progressDoc = outerWindow->GetDoc();
+ if (!progressDoc || 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) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnSecurityChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aState) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCachePendingUpdate::OnContentBlockingEvent(
+ nsIWebProgress* aWebProgress, nsIRequest* aRequest, uint32_t aEvent) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(nsOfflineCacheUpdateService, nsIOfflineCacheUpdateService,
+ nsIObserver, nsISupportsWeakReference)
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService <public>
+//-----------------------------------------------------------------------------
+
+nsOfflineCacheUpdateService::nsOfflineCacheUpdateService()
+ : mDisabled(false), mUpdateRunning(false) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService() {
+ MOZ_ASSERT(gOfflineCacheUpdateService == this);
+ gOfflineCacheUpdateService = nullptr;
+
+ delete mAllowedDomains;
+ mAllowedDomains = 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);
+
+ gOfflineCacheUpdateService = this;
+
+ return NS_OK;
+}
+
+/* static */
+already_AddRefed<nsOfflineCacheUpdateService>
+nsOfflineCacheUpdateService::GetInstance() {
+ if (!gOfflineCacheUpdateService) {
+ auto serv = MakeRefPtr<nsOfflineCacheUpdateService>();
+ if (NS_FAILED(serv->Init())) serv = nullptr;
+ MOZ_ASSERT(gOfflineCacheUpdateService == serv.get());
+ return serv.forget();
+ }
+
+ return do_AddRef(gOfflineCacheUpdateService);
+}
+
+/* static */
+nsOfflineCacheUpdateService* nsOfflineCacheUpdateService::EnsureService() {
+ if (!gOfflineCacheUpdateService) {
+ // Make the service manager hold a long-lived reference to the service
+ nsCOMPtr<nsIOfflineCacheUpdateService> service =
+ components::OfflineCacheUpdate::Service();
+ Unused << service;
+ }
+
+ 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,
+ Document* aDocument) {
+ LOG(
+ ("nsOfflineCacheUpdateService::ScheduleOnDocumentStop [%p, "
+ "manifestURI=%p, documentURI=%p doc=%p]",
+ this, aManifestURI, aDocumentURI, aDocument));
+
+ nsCOMPtr<nsIWebProgress> progress =
+ do_QueryInterface(aDocument->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=%zu]", this,
+ mUpdates.Length()));
+
+ if (mDisabled) return NS_ERROR_ABORT;
+
+ if (mUpdateRunning) return NS_OK;
+
+ if (mUpdates.Length() > 0) {
+ mUpdateRunning = true;
+
+ 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,
+ Document* 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.
+ 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;
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOfflineCacheUpdateService::nsIOfflineCacheUpdateService
+//-----------------------------------------------------------------------------
+
+static nsresult OfflineAppPermForPrincipal(nsIPrincipal* aPrincipal,
+ bool pinned, bool* aAllowed) {
+ *aAllowed = false;
+
+ if (!StaticPrefs::browser_cache_offline_enable()) {
+ return NS_OK;
+ }
+
+ if (!StaticPrefs::browser_cache_offline_storage_enable()) {
+ return NS_OK;
+ }
+
+ if (!aPrincipal) return NS_ERROR_INVALID_ARG;
+
+ nsCOMPtr<nsIURI> uri;
+ // Casting to BasePrincipal, as we can't get InnerMost URI otherwise
+ auto* basePrincipal = BasePrincipal::Cast(aPrincipal);
+ basePrincipal->GetURI(getter_AddRefs(uri));
+
+ if (!uri) return NS_OK;
+
+ nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
+ if (!innerURI) return NS_OK;
+
+ // only https applications can use offline APIs.
+ if (!innerURI->SchemeIs("https")) {
+ return NS_OK;
+ }
+
+ nsAutoCString domain;
+ nsresult 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 nsLiteralCString permName = pinned ? "pin-app"_ns : "offline-app"_ns;
+ permissionManager->TestExactPermissionFromPrincipal(aPrincipal, permName,
+ &perm);
+
+ if (perm == nsIPermissionManager::ALLOW_ACTION ||
+ perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) {
+ *aAllowed = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OfflineAppAllowed(nsIPrincipal* aPrincipal,
+ bool* aAllowed) {
+ return OfflineAppPermForPrincipal(aPrincipal, false, aAllowed);
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI* aURI,
+ bool* aAllowed) {
+ OriginAttributes attrs;
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateContentPrincipal(aURI, attrs);
+ return OfflineAppPermForPrincipal(principal, false, aAllowed);
+}
+
+nsresult nsOfflineCacheUpdateService::OfflineAppPinnedForURI(
+ nsIURI* aDocumentURI, bool* aPinned) {
+ OriginAttributes attrs;
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateContentPrincipal(aDocumentURI, attrs);
+ return OfflineAppPermForPrincipal(principal, true, aPinned);
+}
+
+NS_IMETHODIMP
+nsOfflineCacheUpdateService::AllowOfflineApp(nsIPrincipal* aPrincipal) {
+ nsresult rv;
+
+ if (!StaticPrefs::browser_cache_offline_enable()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (!StaticPrefs::browser_cache_offline_storage_enable()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ // Casting to BasePrincipal, as we can't get InnerMost URI otherwise
+ auto* basePrincipal = BasePrincipal::Cast(aPrincipal);
+ basePrincipal->GetURI(getter_AddRefs(uri));
+
+ if (!uri) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
+ if (!innerURI) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // if http then we should prevent this cache
+ if (innerURI->SchemeIs("http")) {
+ 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"_ns, nsIPermissionManager::ALLOW_ACTION,
+ nsIPermissionManager::EXPIRE_NEVER, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}