diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 667708c60b4d..7eecea02b67d 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -282,6 +282,7 @@ #include "nsDeviceContext.h" #include "nsDocShell.h" #include "nsDocShellLoadTypes.h" +#include "nsEffectiveTLDService.h" #include "nsError.h" #include "nsEscape.h" #include "nsFocusManager.h" @@ -17126,18 +17127,65 @@ already_AddRefed Document::RequestStorageAccessUnderSite( promise->MaybeRejectWithUndefined(); return promise.forget(); } - + nsCOMPtr principal(NodePrincipal()); - // Set a permission in the parent process that this document wants storage - // access under the argument's site, resolving our returned promise on success - ContentChild* cc = ContentChild::GetSingleton(); - if (!cc) { - // TODO(bug 1778561): Make this work in non-content processes. + // Test if the permission this is requesting is already set + nsCOMPtr argumentPrincipal = + BasePrincipal::CreateContentPrincipal( + siteURI, NodePrincipal()->OriginAttributesRef()); + if (!argumentPrincipal) { + this->ConsumeTransientUserGestureActivation(); promise->MaybeRejectWithUndefined(); return promise.forget(); } - cc->SendSetAllowStorageAccessRequestFlag(NodePrincipal(), siteURI) + nsCString originNoSuffix; + rv = NodePrincipal()->GetOriginNoSuffix(originNoSuffix); + if (NS_WARN_IF(NS_FAILED(rv))) { + promise->MaybeRejectWithUndefined(); + return promise.forget(); + } + + ContentChild* cc = ContentChild::GetSingleton(); + MOZ_ASSERT(cc); + RefPtr self(this); + cc->SendTestStorageAccessPermission(IPC::Principal(argumentPrincipal), + originNoSuffix) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [promise, siteURI, + self](const ContentChild::TestStorageAccessPermissionPromise:: + ResolveValueType& aResult) { + if (aResult) { + return StorageAccessAPIHelper:: + StorageAccessPermissionGrantPromise::CreateAndResolve( + StorageAccessAPIHelper::eAllow, __func__); + } + // Get a grant for the storage access permission that will be set + // when this is completed in the embedding context + nsCString serializedSite; + RefPtr etld = + nsEffectiveTLDService::GetInstance(); + if (!etld) { + return StorageAccessAPIHelper:: + StorageAccessPermissionGrantPromise::CreateAndReject( + false, __func__); + } + nsresult rv = etld->GetSite(siteURI, serializedSite); + if (NS_FAILED(rv)) { + return StorageAccessAPIHelper:: + StorageAccessPermissionGrantPromise::CreateAndReject( + false, __func__); + } + return self->CreatePermissionGrantPromise( + self->GetInnerWindow(), self->NodePrincipal(), true, + Some(serializedSite))(); + }, + [](const ContentChild::TestStorageAccessPermissionPromise:: + RejectValueType& aResult) { + return StorageAccessAPIHelper::StorageAccessPermissionGrantPromise:: + CreateAndReject(false, __func__); + }) ->Then( GetCurrentSerialEventTarget(), __func__, [promise, principal, siteURI](int result) { @@ -17145,11 +17193,26 @@ already_AddRefed Document::RequestStorageAccessUnderSite( if (!cc) { // TODO(bug 1778561): Make this work in non-content processes. promise->MaybeRejectWithUndefined(); + return; } + // Set a permission in the parent process that this document wants + // storage access under the argument's site, resolving our returned + // promise on success + cc->SendSetAllowStorageAccessRequestFlag(principal, siteURI) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [promise](bool success) { + if (success) { + promise->MaybeResolveWithUndefined(); + } else { + promise->MaybeRejectWithUndefined(); + } + }, + [promise](mozilla::ipc::ResponseRejectReason reason) { + promise->MaybeRejectWithUndefined(); + }); }, - [promise](mozilla::ipc::ResponseRejectReason reason) { - promise->MaybeRejectWithUndefined(); - }); + [promise](bool result) { promise->MaybeRejectWithUndefined(); }); // Return the promise that is resolved in the async handler above return promise.forget(); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 778144a02b0b..23d56eb7618b 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -6752,6 +6752,38 @@ mozilla::ipc::IPCResult ContentParent::RecvTestCookiePermissionDecided( return IPC_OK(); } +mozilla::ipc::IPCResult ContentParent::RecvTestStorageAccessPermission( + const Principal& aEmbeddingPrincipal, const nsCString& aEmbeddedOrigin, + const TestStorageAccessPermissionResolver&& aResolver) { + // Get the permission manager and build the key. + RefPtr permManager = PermissionManager::GetInstance(); + if (!permManager) { + aResolver(Nothing()); + return IPC_OK(); + } + nsCString requestPermissionKey; + AntiTrackingUtils::CreateStoragePermissionKey(aEmbeddedOrigin, + requestPermissionKey); + + // Test the permission + uint32_t access = nsIPermissionManager::UNKNOWN_ACTION; + nsresult rv = permManager->TestPermissionFromPrincipal( + aEmbeddingPrincipal, requestPermissionKey, &access); + if (NS_FAILED(rv)) { + aResolver(Nothing()); + return IPC_OK(); + } + if (access == nsIPermissionManager::ALLOW_ACTION) { + aResolver(Some(true)); + } else if (access == nsIPermissionManager::DENY_ACTION) { + aResolver(Some(false)); + } else { + aResolver(Nothing()); + } + + return IPC_OK(); +} + mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaPlaybackChanged( const MaybeDiscarded& aContext, MediaPlaybackState aState) { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index e51dd364507e..7a1c09145cbc 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -1274,6 +1274,10 @@ class ContentParent final : public PContentParent, const MaybeDiscarded& aContext, nsIPrincipal* aPrincipal, const TestCookiePermissionDecidedResolver&& aResolver); + mozilla::ipc::IPCResult RecvTestStorageAccessPermission( + const Principal& aEmbeddingPrincipal, const nsCString& aEmbeddedOrigin, + const TestStorageAccessPermissionResolver&& aResolver); + mozilla::ipc::IPCResult RecvNotifyMediaPlaybackChanged( const MaybeDiscarded& aContext, MediaPlaybackState aState); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 4d559c30c86d..7e41638c6cf2 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1669,6 +1669,10 @@ parent: nsIPrincipal aPrincipal) returns (bool? allowed); + async TestStorageAccessPermission(Principal aEmbeddingPrincipal, + nsCString aEmbeddedOrigin) + returns (bool? allowed); + /** * When media element's controlled state changed in the content process, we * have to notify the chrome process in order to update the status of the diff --git a/toolkit/components/antitracking/StorageAccessAPIHelper.cpp b/toolkit/components/antitracking/StorageAccessAPIHelper.cpp index 5d5d4ff935f0..7d3535d955fe 100644 --- a/toolkit/components/antitracking/StorageAccessAPIHelper.cpp +++ b/toolkit/components/antitracking/StorageAccessAPIHelper.cpp @@ -984,8 +984,8 @@ StorageAccessAPIHelper::RequestStorageAccessAsyncHelper( aInnerWindow, principal, aHasUserInteraction, Nothing()); // Try to allow access for the given principal. - return StorageAccessAPIHelper::AllowAccessFor(principal, aBrowsingContext, - aNotifier, performPermissionGrant); + return StorageAccessAPIHelper::AllowAccessFor( + principal, aBrowsingContext, aNotifier, performPermissionGrant); } // There are two methods to handle permission update: diff --git a/toolkit/components/antitracking/StorageAccessAPIHelper.h b/toolkit/components/antitracking/StorageAccessAPIHelper.h index 471ff5e16ad1..9be39df1c545 100644 --- a/toolkit/components/antitracking/StorageAccessAPIHelper.h +++ b/toolkit/components/antitracking/StorageAccessAPIHelper.h @@ -163,7 +163,8 @@ class StorageAccessAPIHelper final { // trying to perform an "autogrant" if aRequireGrant is true. // This will return a promise whose values correspond to those of a // ContentBlocking::AllowAccessFor call that ends the function. - static RefPtr RequestStorageAccessAsyncHelper( + static RefPtr + RequestStorageAccessAsyncHelper( dom::Document* aDocument, nsPIDOMWindowInner* aInnerWindow, dom::BrowsingContext* aBrowsingContext, nsIPrincipal* aPrincipal, bool aHasUserInteraction,