diff --git a/browser/modules/PermissionUI.sys.mjs b/browser/modules/PermissionUI.sys.mjs index 8fc173886b2b..ac8b441c5518 100644 --- a/browser/modules/PermissionUI.sys.mjs +++ b/browser/modules/PermissionUI.sys.mjs @@ -1298,20 +1298,33 @@ class MIDIPermissionPrompt extends SitePermsAddonInstallRequest { } class StorageAccessPermissionPrompt extends PermissionPromptForRequest { + #permissionKey; + constructor(request) { super(); this.request = request; this.siteOption = null; + this.#permissionKey = `3rdPartyStorage${lazy.SitePermissions.PERM_KEY_DELIMITER}${this.principal.origin}`; let types = this.request.types.QueryInterface(Ci.nsIArray); let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); let options = perm.options.QueryInterface(Ci.nsIArray); - // If we have an option, we are in a call from requestStorageAccessUnderSite - // which means that the embedding principal is not the current top-level. - // Instead we have to grab the Site string out of the option and use that - // in the UI. - if (options.length) { - this.siteOption = options.queryElementAt(0, Ci.nsISupportsString).data; + // If we have an option, the permission request is different in some way. + // We may be in a call from requestStorageAccessUnderSite or a frame-scoped + // request, which means that the embedding principal is not the current top-level + // or the permission key is different. + if (options.length != 2) { + return; + } + + let topLevelOption = options.queryElementAt(0, Ci.nsISupportsString).data; + if (topLevelOption) { + this.siteOption = topLevelOption; + } + let frameOption = options.queryElementAt(1, Ci.nsISupportsString).data; + if (frameOption) { + // We replace the permission key with a frame-specific one that only has a site after the delimiter + this.#permissionKey = `3rdPartyFrameStorage${lazy.SitePermissions.PERM_KEY_DELIMITER}${this.principal.siteOrigin}`; } } @@ -1325,7 +1338,7 @@ class StorageAccessPermissionPrompt extends PermissionPromptForRequest { get permissionKey() { // Make sure this name is unique per each third-party tracker - return `3rdPartyStorage${lazy.SitePermissions.PERM_KEY_DELIMITER}${this.principal.origin}`; + return this.#permissionKey; } get temporaryPermissionURI() { diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 81313ed8a026..8c9b56e16af3 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -17178,7 +17178,7 @@ Document::CreatePermissionGrantPromise( Private(__func__); RefPtr sapr = StorageAccessPermissionRequest::Create( - inner, principal, aTopLevelBaseDomain, + inner, principal, aTopLevelBaseDomain, false, // Allow [p] { Telemetry::AccumulateCategorical( diff --git a/dom/base/StorageAccessPermissionRequest.cpp b/dom/base/StorageAccessPermissionRequest.cpp index 094cd8739377..ae7f57b7dcfc 100644 --- a/dom/base/StorageAccessPermissionRequest.cpp +++ b/dom/base/StorageAccessPermissionRequest.cpp @@ -19,16 +19,21 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(StorageAccessPermissionRequest, StorageAccessPermissionRequest::StorageAccessPermissionRequest( nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal, - const Maybe& aTopLevelBaseDomain, AllowCallback&& aAllowCallback, - CancelCallback&& aCancelCallback) + const Maybe& aTopLevelBaseDomain, bool aFrameOnly, + AllowCallback&& aAllowCallback, CancelCallback&& aCancelCallback) : ContentPermissionRequestBase(aNodePrincipal, aWindow, "dom.storage_access"_ns, "storage-access"_ns), mAllowCallback(std::move(aAllowCallback)), mCancelCallback(std::move(aCancelCallback)), mCallbackCalled(false) { + mOptions.SetLength(2); if (aTopLevelBaseDomain.isSome()) { - mOptions.AppendElement(NS_ConvertUTF8toUTF16(aTopLevelBaseDomain.value())); + nsCString option = aTopLevelBaseDomain.value(); + mOptions.ElementAt(0) = NS_ConvertUTF8toUTF16(option); + } + if (aFrameOnly) { + mOptions.ElementAt(1) = u"1"_ns; } mPermissionRequests.AppendElement(PermissionRequest(mType, mOptions)); } @@ -119,15 +124,15 @@ StorageAccessPermissionRequest::Create(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal, AllowCallback&& aAllowCallback, CancelCallback&& aCancelCallback) { - return Create(aWindow, aPrincipal, Nothing(), std::move(aAllowCallback), + return Create(aWindow, aPrincipal, Nothing(), true, std::move(aAllowCallback), std::move(aCancelCallback)); } already_AddRefed StorageAccessPermissionRequest::Create( nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal, - const Maybe& aTopLevelBaseDomain, AllowCallback&& aAllowCallback, - CancelCallback&& aCancelCallback) { + const Maybe& aTopLevelBaseDomain, bool aFrameOnly, + AllowCallback&& aAllowCallback, CancelCallback&& aCancelCallback) { if (!aWindow) { return nullptr; } @@ -138,8 +143,8 @@ StorageAccessPermissionRequest::Create( RefPtr request = new StorageAccessPermissionRequest( - aWindow, aPrincipal, aTopLevelBaseDomain, std::move(aAllowCallback), - std::move(aCancelCallback)); + aWindow, aPrincipal, aTopLevelBaseDomain, aFrameOnly, + std::move(aAllowCallback), std::move(aCancelCallback)); return request.forget(); } diff --git a/dom/base/StorageAccessPermissionRequest.h b/dom/base/StorageAccessPermissionRequest.h index dec3054d659a..8098037534b2 100644 --- a/dom/base/StorageAccessPermissionRequest.h +++ b/dom/base/StorageAccessPermissionRequest.h @@ -47,7 +47,7 @@ class StorageAccessPermissionRequest final // of aPrincipal's Top browsing context is used. static already_AddRefed Create( nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal, - const Maybe& aTopLevelBaseDomain, + const Maybe& aTopLevelBaseDomain, bool aFrameOnly, AllowCallback&& aAllowCallback, CancelCallback&& aCancelCallback); using AutoGrantDelayPromise = MozPromise; @@ -57,6 +57,7 @@ class StorageAccessPermissionRequest final StorageAccessPermissionRequest(nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal, const Maybe& aTopLevelBaseDomain, + bool aFrameOnly, AllowCallback&& aAllowCallback, CancelCallback&& aCancelCallback); ~StorageAccessPermissionRequest() {