Bug 1193373 - Support Permissions API in Worker Context, r=manuel,webidl,asuth,smaug

This commit exposes the Permissions API to DOM Workers. It achieves this goal
by introducing a thread-safe bridge between `PermissionStatus` and the
`PermissionObserver`: the `PermissionStatusSink` object.

Actors:
- The `PermissionObserver` is a main-thread-only singleton that monitors
  permission change events and propagates the notification to the right sink
  objects.
- The `PermissionStatus` is the DOM object exposed to the global. It's not
  thread-safe.
- The `PermissionStatusSink` is the new bridge introduced by this commit.

The `PermissionStatusSink` lifetime:
- This object is kept alive on the current thread by the `PermissionStatus` and
  on the main thread by the `PermissionObserver`.
- The `PermissionStatus` creates the object on its creation thread. When
  `PermissionStatus` object is released (or disconnected from the owner, it
  disentangles itself from the `PermissionStatusSink`. The disentangle
  operation triggers the un-registration procedure from the
  `PermissionObserver` on the main thread.
- A weak `WorkerRef` is used to monitor the worker's lifetime.

Permission change notification:
- When the  `PermissionObserver` is notified for a permission-change event, it
  notifies all the `PermissionStatusSink`. This happens on the main thread (see
  `MaybeUpdatedByOnMainThread` and `MaybeUpdatedByNotifyOnlyOnMainThread`).
- Using `MozPromise`, the `PermissionStatusSink` computes the permission action
  (`PermissionChangedOnMainThread`) on the main thread, then informs the
  parent `PermissionStatus` object on its creation thread.
- The `PermissionStatus` object converts the action to the DOM
  `PermissionState` and dispatches an event.

Differential Revision: https://phabricator.services.mozilla.com/D224594
This commit is contained in:
Andrea Marchesini 2024-10-11 06:53:48 +00:00
parent c857667f85
commit 8ae9152b88
29 changed files with 619 additions and 327 deletions

View File

@ -516,7 +516,7 @@ Permissions* Navigator::GetPermissions(ErrorResult& aRv) {
}
if (!mPermissions) {
mPermissions = new Permissions(mWindow);
mPermissions = new Permissions(mWindow->AsGlobal());
}
return mPermissions;

View File

@ -11,9 +11,9 @@
namespace mozilla::dom {
MidiPermissionStatus::MidiPermissionStatus(nsPIDOMWindowInner* aWindow,
MidiPermissionStatus::MidiPermissionStatus(nsIGlobalObject* aGlobal,
bool aSysex)
: PermissionStatus(aWindow, PermissionName::Midi), mSysex(aSysex) {}
: PermissionStatus(aGlobal, PermissionName::Midi), mSysex(aSysex) {}
nsLiteralCString MidiPermissionStatus::GetPermissionType() const {
return mSysex ? "midi-sysex"_ns : "midi"_ns;

View File

@ -13,7 +13,7 @@ namespace mozilla::dom {
class MidiPermissionStatus final : public PermissionStatus {
public:
MidiPermissionStatus(nsPIDOMWindowInner* aWindow, bool aSysex);
MidiPermissionStatus(nsIGlobalObject* aGlobal, bool aSysex);
private:
~MidiPermissionStatus() {}

View File

@ -6,12 +6,11 @@
#include "PermissionObserver.h"
#include "mozilla/dom/PermissionStatus.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/Services.h"
#include "mozilla/UniquePtr.h"
#include "nsIObserverService.h"
#include "nsIPermission.h"
#include "PermissionStatusSink.h"
#include "PermissionUtils.h"
namespace mozilla::dom {
@ -22,9 +21,13 @@ PermissionObserver* gInstance = nullptr;
NS_IMPL_ISUPPORTS(PermissionObserver, nsIObserver, nsISupportsWeakReference)
PermissionObserver::PermissionObserver() { MOZ_ASSERT(!gInstance); }
PermissionObserver::PermissionObserver() {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
MOZ_ASSERT(!gInstance);
}
PermissionObserver::~PermissionObserver() {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
MOZ_ASSERT(mSinks.IsEmpty());
MOZ_ASSERT(gInstance == this);
@ -33,6 +36,8 @@ PermissionObserver::~PermissionObserver() {
/* static */
already_AddRefed<PermissionObserver> PermissionObserver::GetInstance() {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
RefPtr<PermissionObserver> instance = gInstance;
if (!instance) {
instance = new PermissionObserver();
@ -58,14 +63,16 @@ already_AddRefed<PermissionObserver> PermissionObserver::GetInstance() {
return instance.forget();
}
void PermissionObserver::AddSink(PermissionStatus* aSink) {
void PermissionObserver::AddSink(PermissionStatusSink* aSink) {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
MOZ_ASSERT(aSink);
MOZ_ASSERT(!mSinks.Contains(aSink));
mSinks.AppendElement(aSink);
}
void PermissionObserver::RemoveSink(PermissionStatus* aSink) {
void PermissionObserver::RemoveSink(PermissionStatusSink* aSink) {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
MOZ_ASSERT(aSink);
MOZ_ASSERT(mSinks.Contains(aSink));
@ -75,6 +82,7 @@ void PermissionObserver::RemoveSink(PermissionStatus* aSink) {
NS_IMETHODIMP
PermissionObserver::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
MOZ_ASSERT(!strcmp(aTopic, "perm-changed") ||
!strcmp(aTopic, "perm-changed-notify-only"));
@ -102,15 +110,15 @@ PermissionObserver::Observe(nsISupports* aSubject, const char* aTopic,
Maybe<PermissionName> permission = TypeToPermissionName(type);
if (permission) {
for (auto* sink : mSinks) {
if (sink->mName != permission.value()) {
for (PermissionStatusSink* sink : mSinks) {
if (sink->Name() != permission.value()) {
continue;
}
// Check for permissions that are changed for this sink's principal
// via the "perm-changed" notification. These permissions affect
// the window the sink (PermissionStatus) is held in directly.
if (perm && sink->MaybeUpdatedBy(perm)) {
sink->PermissionChanged();
if (perm && sink->MaybeUpdatedByOnMainThread(perm)) {
sink->PermissionChangedOnMainThread();
}
// Check for permissions that are changed for this sink's principal
// via the "perm-changed-notify-only" notification. These permissions
@ -119,8 +127,9 @@ PermissionObserver::Observe(nsISupports* aSubject, const char* aTopic,
// example, a "3rdPartyFrameStorage^https://example.com" permission would
// return true on these checks where sink is in a window that is same-site
// with https://example.com.
if (innerWindow && sink->MaybeUpdatedByNotifyOnly(innerWindow)) {
sink->PermissionChanged();
if (innerWindow &&
sink->MaybeUpdatedByNotifyOnlyOnMainThread(innerWindow)) {
sink->PermissionChangedOnMainThread();
}
}
}

View File

@ -7,16 +7,13 @@
#ifndef mozilla_dom_PermissionObserver_h_
#define mozilla_dom_PermissionObserver_h_
#include "mozilla/dom/PermissionsBinding.h"
#include "nsIObserver.h"
#include "nsIPrincipal.h"
#include "nsTArray.h"
#include "nsWeakReference.h"
namespace mozilla::dom {
class PermissionStatus;
class PermissionStatusSink;
// Singleton that watches for perm-changed notifications in order to notify
// PermissionStatus objects.
@ -28,14 +25,14 @@ class PermissionObserver final : public nsIObserver,
static already_AddRefed<PermissionObserver> GetInstance();
void AddSink(PermissionStatus* aObs);
void RemoveSink(PermissionStatus* aObs);
void AddSink(PermissionStatusSink* aSink);
void RemoveSink(PermissionStatusSink* aSink);
private:
PermissionObserver();
virtual ~PermissionObserver();
nsTArray<PermissionStatus*> mSinks;
nsTArray<RefPtr<PermissionStatusSink>> mSinks;
};
} // namespace mozilla::dom

View File

@ -5,21 +5,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/PermissionStatus.h"
#include "mozilla/PermissionDelegateHandler.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Permission.h"
#include "mozilla/Services.h"
#include "nsIPermissionManager.h"
#include "PermissionObserver.h"
#include "PermissionUtils.h"
#include "PermissionStatusSink.h"
#include "nsGlobalWindowInner.h"
namespace mozilla::dom {
PermissionStatus::PermissionStatus(nsPIDOMWindowInner* aWindow,
PermissionStatus::PermissionStatus(nsIGlobalObject* aGlobal,
PermissionName aName)
: DOMEventTargetHelper(aWindow),
: DOMEventTargetHelper(aGlobal),
mName(aName),
mState(PermissionState::Denied) {
KeepAliveIfHasListenersFor(nsGkAtoms::onchange);
@ -28,24 +27,26 @@ PermissionStatus::PermissionStatus(nsPIDOMWindowInner* aWindow,
// https://w3c.github.io/permissions/#onchange-attribute and
// https://w3c.github.io/permissions/#query-method
RefPtr<PermissionStatus::SimplePromise> PermissionStatus::Init() {
// Covers the onchange part
// Whenever the user agent is aware that the state of a PermissionStatus
// instance status has changed: ...
// (The observer calls PermissionChanged() to do the steps)
mObserver = PermissionObserver::GetInstance();
if (NS_WARN_IF(!mObserver)) {
return SimplePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
mSink = CreateSink();
MOZ_ASSERT(mSink);
mObserver->AddSink(this);
return mSink->Init()->Then(
GetCurrentSerialEventTarget(), __func__,
[self = RefPtr(this)](const PermissionStatusSink::PermissionStatePromise::
ResolveOrRejectValue& aResult) {
if (aResult.IsResolve()) {
self->mState = self->ComputeStateFromAction(aResult.ResolveValue());
return SimplePromise::CreateAndResolve(NS_OK, __func__);
}
// Covers the query part (Step 8.2 - 8.4)
return UpdateState();
return SimplePromise::CreateAndReject(aResult.RejectValue(), __func__);
});
}
PermissionStatus::~PermissionStatus() {
if (mObserver) {
mObserver->RemoveSink(this);
if (mSink) {
mSink->Disentangle();
mSink = nullptr;
}
}
@ -58,111 +59,28 @@ nsLiteralCString PermissionStatus::GetPermissionType() const {
return PermissionNameToType(mName);
}
// Covers the calling part of "permission query algorithm" of query() method and
// update steps, which calls
// https://w3c.github.io/permissions/#dfn-default-permission-query-algorithm
// and then https://w3c.github.io/permissions/#dfn-permission-state
RefPtr<PermissionStatus::SimplePromise> PermissionStatus::UpdateState() {
// Step 1: If settings wasn't passed, set it to the current settings object.
// Step 2: If settings is a non-secure context, return "denied".
// XXX(krosylight): No such steps here, and no WPT coverage?
// The permission handler covers the rest of the steps, although the model
// does not exactly match what the spec has. (Not passing "permission key" for
// example)
RefPtr<nsGlobalWindowInner> window = GetOwnerWindow();
if (NS_WARN_IF(!window)) {
return SimplePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
RefPtr<Document> document = window->GetExtantDoc();
if (NS_WARN_IF(!document)) {
return SimplePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
uint32_t action = nsIPermissionManager::DENY_ACTION;
PermissionDelegateHandler* permissionHandler =
document->GetPermissionDelegateHandler();
if (NS_WARN_IF(!permissionHandler)) {
return SimplePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
nsresult rv = permissionHandler->GetPermissionForPermissionsAPI(
GetPermissionType(), &action);
if (NS_WARN_IF(NS_FAILED(rv))) {
return SimplePromise::CreateAndReject(rv, __func__);
}
mState = ActionToPermissionState(action, mName, *document);
return SimplePromise::CreateAndResolve(NS_OK, __func__);
}
bool PermissionStatus::MaybeUpdatedBy(nsIPermission* aPermission) const {
NS_ENSURE_TRUE(aPermission, false);
RefPtr<nsGlobalWindowInner> window = GetOwnerWindow();
if (NS_WARN_IF(!window)) {
return false;
}
Document* doc = window->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return false;
}
nsCOMPtr<nsIPrincipal> principal =
Permission::ClonePrincipalForPermission(doc->NodePrincipal());
NS_ENSURE_TRUE(principal, false);
nsCOMPtr<nsIPrincipal> permissionPrincipal;
aPermission->GetPrincipal(getter_AddRefs(permissionPrincipal));
if (!permissionPrincipal) {
return false;
}
return permissionPrincipal->Equals(principal);
}
bool PermissionStatus::MaybeUpdatedByNotifyOnly(
nsPIDOMWindowInner* aInnerWindow) const {
return false;
}
// https://w3c.github.io/permissions/#dfn-permissionstatus-update-steps
void PermissionStatus::PermissionChanged() {
auto oldState = mState;
RefPtr<PermissionStatus> self(this);
// Step 1: If this's relevant global object is a Window object, then:
// Step 1.1: Let document be status's relevant global object's associated
// Document.
// Step 1.2: If document is null or document is not fully active,
// terminate this algorithm.
// TODO(krosylight): WPT /permissions/non-fully-active.https.html fails
// because we don't do this. See bug 1876470.
void PermissionStatus::PermissionChanged(uint32_t aAction) {
PermissionState newState = ComputeStateFromAction(aAction);
if (mState == newState) {
return;
}
// Step 2 - 3 is covered by UpdateState()
UpdateState()->Then(
GetMainThreadSerialEventTarget(), __func__,
[self, oldState]() {
if (self->mState != oldState) {
// Step 4: Queue a task on the permissions task source to fire an
// event named change at status.
RefPtr<AsyncEventDispatcher> eventDispatcher =
new AsyncEventDispatcher(self.get(), u"change"_ns,
CanBubble::eNo);
eventDispatcher->PostDOMEvent();
}
},
[]() {
mState = newState;
});
// Step 4: Queue a task on the permissions task source to fire an
// event named change at status.
RefPtr<AsyncEventDispatcher> eventDispatcher =
new AsyncEventDispatcher(this, u"change"_ns, CanBubble::eNo);
eventDispatcher->PostDOMEvent();
}
void PermissionStatus::DisconnectFromOwner() {
IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onchange);
if (mObserver) {
mObserver->RemoveSink(this);
mObserver = nullptr;
if (mSink) {
mSink->Disentangle();
mSink = nullptr;
}
DOMEventTargetHelper::DisconnectFromOwner();
@ -172,4 +90,19 @@ void PermissionStatus::GetType(nsACString& aName) const {
aName.Assign(GetPermissionType());
}
already_AddRefed<PermissionStatusSink> PermissionStatus::CreateSink() {
RefPtr<PermissionStatusSink> sink =
new PermissionStatusSink(this, mName, GetPermissionType());
return sink.forget();
}
PermissionState PermissionStatus::ComputeStateFromAction(uint32_t aAction) {
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
if (NS_WARN_IF(!global)) {
return PermissionState::Denied;
}
return ActionToPermissionState(aAction, mName, global);
}
} // namespace mozilla::dom

View File

@ -11,19 +11,18 @@
#include "mozilla/dom/PermissionStatusBinding.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/MozPromise.h"
#include "nsIPermission.h"
namespace mozilla::dom {
class PermissionObserver;
class PermissionStatusSink;
class PermissionStatus : public DOMEventTargetHelper {
friend class PermissionObserver;
friend class PermissionStatusSink;
public:
using SimplePromise = MozPromise<nsresult, nsresult, true>;
PermissionStatus(nsPIDOMWindowInner* aWindow, PermissionName aName);
PermissionStatus(nsIGlobalObject* aGlobal, PermissionName aName);
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -57,23 +56,14 @@ class PermissionStatus : public DOMEventTargetHelper {
virtual nsLiteralCString GetPermissionType() const;
private:
virtual RefPtr<SimplePromise> UpdateState();
virtual already_AddRefed<PermissionStatusSink> CreateSink();
// These functions should be called when an permission is updated which may
// change the state of this PermissionStatus. MaybeUpdatedBy accepts the
// permission object itself that is update. When the permission's key is not
// same-origin with this object's owner window, such as for secondary-keyed
// permissions like `3rdPartyFrameStorage^...`, MaybeUpdatedByNotifyOnly will
// be called with the updated window as an argument. MaybeUpdatedByNotifyOnly
// must be defined by PermissionStatus inheritors that are double-keyed.
virtual bool MaybeUpdatedBy(nsIPermission* aPermission) const;
virtual bool MaybeUpdatedByNotifyOnly(nsPIDOMWindowInner* aInnerWindow) const;
void PermissionChanged(uint32_t aAction);
void PermissionChanged();
PermissionState ComputeStateFromAction(uint32_t aAction);
PermissionName mName;
RefPtr<PermissionObserver> mObserver;
RefPtr<PermissionStatusSink> mSink;
protected:
PermissionState mState;

View File

@ -0,0 +1,231 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "PermissionStatusSink.h"
#include "PermissionObserver.h"
#include "PermissionStatus.h"
#include "mozilla/Permission.h"
#include "mozilla/PermissionDelegateHandler.h"
#include "mozilla/PermissionManager.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRef.h"
namespace mozilla::dom {
PermissionStatusSink::PermissionStatusSink(PermissionStatus* aPermissionStatus,
PermissionName aPermissionName,
const nsACString& aPermissionType)
: mSerialEventTarget(NS_GetCurrentThread()),
mPermissionStatus(aPermissionStatus),
mMutex("PermissionStatusSink::mMutex"),
mPermissionName(aPermissionName),
mPermissionType(aPermissionType) {
MOZ_ASSERT(aPermissionStatus);
MOZ_ASSERT(mSerialEventTarget);
nsCOMPtr<nsIGlobalObject> global = aPermissionStatus->GetOwnerGlobal();
if (NS_WARN_IF(!global)) {
return;
}
nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
if (NS_WARN_IF(!principal)) {
return;
}
mPrincipalForPermission = Permission::ClonePrincipalForPermission(principal);
}
PermissionStatusSink::~PermissionStatusSink() = default;
RefPtr<PermissionStatusSink::PermissionStatePromise>
PermissionStatusSink::Init() {
if (!NS_IsMainThread()) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
MutexAutoLock lock(mMutex);
mWorkerRef = WeakWorkerRef::Create(
workerPrivate, [self = RefPtr(this)] { self->Disentangle(); });
}
return InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
[self = RefPtr(this)] {
MOZ_ASSERT(!self->mObserver);
// Covers the onchange part
// Whenever the user agent is aware that the state of a
// PermissionStatus instance status has changed: ... (The
// observer calls PermissionChanged() to do the steps)
self->mObserver = PermissionObserver::GetInstance();
if (NS_WARN_IF(!self->mObserver)) {
return PermissionStatePromise::CreateAndReject(
NS_ERROR_FAILURE, __func__);
}
self->mObserver->AddSink(self);
// Covers the query part (Step 8.2 - 8.4)
return self->ComputeStateOnMainThread();
});
}
bool PermissionStatusSink::MaybeUpdatedByOnMainThread(
nsIPermission* aPermission) {
MOZ_ASSERT(NS_IsMainThread());
if (!mPrincipalForPermission) {
return false;
}
nsCOMPtr<nsIPrincipal> permissionPrincipal;
aPermission->GetPrincipal(getter_AddRefs(permissionPrincipal));
if (!permissionPrincipal) {
return false;
}
return mPrincipalForPermission->Equals(permissionPrincipal);
}
bool PermissionStatusSink::MaybeUpdatedByNotifyOnlyOnMainThread(
nsPIDOMWindowInner* aInnerWindow) {
MOZ_ASSERT(NS_IsMainThread());
return false;
}
void PermissionStatusSink::PermissionChangedOnMainThread() {
MOZ_ASSERT(NS_IsMainThread());
ComputeStateOnMainThread()->Then(
mSerialEventTarget, __func__,
[self = RefPtr(this)](
const PermissionStatePromise::ResolveOrRejectValue& aResult) {
if (aResult.IsResolve() && self->mPermissionStatus) {
self->mPermissionStatus->PermissionChanged(aResult.ResolveValue());
}
});
}
void PermissionStatusSink::Disentangle() {
MOZ_ASSERT(mSerialEventTarget->IsOnCurrentThread());
mPermissionStatus = nullptr;
{
MutexAutoLock lock(mMutex);
mWorkerRef = nullptr;
}
NS_DispatchToMainThread(
NS_NewRunnableFunction(__func__, [self = RefPtr(this)] {
if (self->mObserver) {
self->mObserver->RemoveSink(self);
self->mObserver = nullptr;
}
}));
}
RefPtr<PermissionStatusSink::PermissionStatePromise>
PermissionStatusSink::ComputeStateOnMainThread() {
MOZ_ASSERT(NS_IsMainThread());
// Step 1: If settings wasn't passed, set it to the current settings object.
// Step 2: If settings is a non-secure context, return "denied".
// XXX(krosylight): No such steps here, and no WPT coverage?
// The permission handler covers the rest of the steps, although the model
// does not exactly match what the spec has. (Not passing "permission key" for
// example)
if (mSerialEventTarget->IsOnCurrentThread()) {
if (!mPermissionStatus) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
}
RefPtr<nsGlobalWindowInner> window = mPermissionStatus->GetOwnerWindow();
return ComputeStateOnMainThreadInternal(window);
}
nsCOMPtr<nsPIDOMWindowInner> ancestorWindow;
nsCOMPtr<nsIPrincipal> workerPrincipal;
{
MutexAutoLock lock(mMutex);
if (!mWorkerRef) {
// We have been disentangled.
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
}
// If we have mWorkerRef, we haven't received the WorkerRef notification
// yet.
WorkerPrivate* workerPrivate = mWorkerRef->GetUnsafePrivate();
MOZ_ASSERT(workerPrivate);
ancestorWindow = workerPrivate->GetAncestorWindow();
workerPrincipal = workerPrivate->GetPrincipal();
}
if (ancestorWindow) {
return ComputeStateOnMainThreadInternal(ancestorWindow);
}
if (NS_WARN_IF(!workerPrincipal)) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
RefPtr<nsIPermissionManager> permissionManager =
PermissionManager::GetInstance();
if (!permissionManager) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
uint32_t action = nsIPermissionManager::DENY_ACTION;
nsresult rv = permissionManager->TestPermissionFromPrincipal(
workerPrincipal, mPermissionType, &action);
if (NS_WARN_IF(NS_FAILED(rv))) {
return PermissionStatePromise::CreateAndReject(rv, __func__);
}
return PermissionStatePromise::CreateAndResolve(action, __func__);
}
RefPtr<PermissionStatusSink::PermissionStatePromise>
PermissionStatusSink::ComputeStateOnMainThreadInternal(
nsPIDOMWindowInner* aWindow) {
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aWindow)) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
RefPtr<Document> document = aWindow->GetExtantDoc();
if (NS_WARN_IF(!document)) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
uint32_t action = nsIPermissionManager::DENY_ACTION;
PermissionDelegateHandler* permissionHandler =
document->GetPermissionDelegateHandler();
if (NS_WARN_IF(!permissionHandler)) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
nsresult rv = permissionHandler->GetPermissionForPermissionsAPI(
mPermissionType, &action);
if (NS_WARN_IF(NS_FAILED(rv))) {
return PermissionStatePromise::CreateAndReject(rv, __func__);
}
return PermissionStatePromise::CreateAndResolve(action, __func__);
}
} // namespace mozilla::dom

View File

@ -0,0 +1,81 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef mozilla_dom_PermissionStatusSink_h
#define mozilla_dom_PermissionStatusSink_h
#include "mozilla/dom/PermissionsBinding.h"
#include "mozilla/dom/PermissionStatusBinding.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Mutex.h"
#include "nsIPermission.h"
class nsPIDOMWindowInner;
namespace mozilla::dom {
class PermissionObserver;
class PermissionStatus;
class WeakWorkerRef;
class PermissionStatusSink {
public:
using PermissionStatePromise = MozPromise<uint32_t, nsresult, true>;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PermissionStatusSink)
PermissionStatusSink(PermissionStatus* aPermissionStatus,
PermissionName aPermissionName,
const nsACString& aPermissionType);
RefPtr<PermissionStatePromise> Init();
// These functions should be called when an permission is updated which may
// change the state of this PermissionStatus. MaybeUpdatedByOnMainThread
// accepts the permission object itself that is update. When the permission's
// key is not same-origin with this object's owner window/worker, such as for
// secondary-keyed permissions like `3rdPartyFrameStorage^...`,
// MaybeUpdatedByNotifyOnlyOnMainThread will be called with the updated
// window/worker as an argument. MaybeUpdatedByNotifyOnly must be defined by
// PermissionStatus inheritors that are double-keyed.
virtual bool MaybeUpdatedByOnMainThread(nsIPermission* aPermission);
virtual bool MaybeUpdatedByNotifyOnlyOnMainThread(
nsPIDOMWindowInner* aInnerWindow);
void PermissionChangedOnMainThread();
PermissionName Name() const { return mPermissionName; }
void Disentangle();
protected:
virtual ~PermissionStatusSink();
virtual RefPtr<PermissionStatePromise> ComputeStateOnMainThread();
RefPtr<PermissionStatePromise> ComputeStateOnMainThreadInternal(
nsPIDOMWindowInner* aWindow);
nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
nsCOMPtr<nsIPrincipal> mPrincipalForPermission;
RefPtr<PermissionObserver> mObserver;
RefPtr<PermissionStatus> mPermissionStatus;
Mutex mMutex;
// Protected by mutex.
// Created and released on worker-thread. Used also on main-thread.
RefPtr<WeakWorkerRef> mWorkerRef MOZ_GUARDED_BY(mMutex);
PermissionName mPermissionName;
nsCString mPermissionType;
};
} // namespace mozilla::dom
#endif // mozilla_dom_permissionstatusSink_h

View File

@ -60,7 +60,9 @@ Maybe<PermissionName> TypeToPermissionName(const nsACString& aType) {
}
PermissionState ActionToPermissionState(uint32_t aAction, PermissionName aName,
const Document& aDocument) {
nsIGlobalObject* aGlobal) {
MOZ_ASSERT(aGlobal);
switch (aAction) {
case nsIPermissionManager::ALLOW_ACTION:
return PermissionState::Granted;
@ -71,7 +73,7 @@ PermissionState ActionToPermissionState(uint32_t aAction, PermissionName aName,
case nsIPermissionManager::PROMPT_ACTION:
if ((aName == PermissionName::Camera ||
aName == PermissionName::Microphone) &&
!aDocument.ShouldResistFingerprinting(RFPTarget::MediaDevices)) {
!aGlobal->ShouldResistFingerprinting(RFPTarget::MediaDevices)) {
// A persisted PROMPT_ACTION means the user chose "Always Ask"
// which shows as "granted" to prevent websites from priming the
// user to escalate permission any further.

View File

@ -9,6 +9,7 @@
#include "mozilla/dom/PermissionsBinding.h"
#include "mozilla/dom/PermissionStatusBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/Maybe.h"
namespace mozilla::dom {
@ -27,7 +28,7 @@ const nsLiteralCString& PermissionNameToType(PermissionName aName);
Maybe<PermissionName> TypeToPermissionName(const nsACString& aType);
PermissionState ActionToPermissionState(uint32_t aAction, PermissionName aName,
const Document& aDocument);
nsIGlobalObject* aGlobal);
} // namespace mozilla::dom

View File

@ -27,9 +27,10 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Permissions)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Permissions)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Permissions, mWindow)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Permissions)
Permissions::Permissions(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
Permissions::Permissions(nsIGlobalObject* aGlobal)
: GlobalTeardownObserver(aGlobal) {}
Permissions::~Permissions() = default;
@ -45,7 +46,7 @@ namespace {
// commands
RefPtr<PermissionStatus> CreatePermissionStatus(
JSContext* aCx, JS::Handle<JSObject*> aPermissionDesc,
nsPIDOMWindowInner* aWindow, ErrorResult& aRv) {
nsIGlobalObject* aGlobal, ErrorResult& aRv) {
// Step 2: Let rootDesc be the object permissionDesc refers to, converted to
// an IDL value of type PermissionDescriptor.
PermissionDescriptor rootDesc;
@ -76,16 +77,16 @@ RefPtr<PermissionStatus> CreatePermissionStatus(
return nullptr;
}
return new MidiPermissionStatus(aWindow, midiPerm.mSysex);
return new MidiPermissionStatus(aGlobal, midiPerm.mSysex);
}
case PermissionName::Storage_access:
return new StorageAccessPermissionStatus(aWindow);
return new StorageAccessPermissionStatus(aGlobal);
case PermissionName::Geolocation:
case PermissionName::Notifications:
case PermissionName::Push:
case PermissionName::Persistent_storage:
case PermissionName::Screen_wake_lock:
return new PermissionStatus(aWindow, rootDesc.mName);
return new PermissionStatus(aGlobal, rootDesc.mName);
case PermissionName::Camera:
if (!StaticPrefs::permissions_media_query_enabled()) {
aRv.ThrowTypeError(
@ -93,7 +94,7 @@ RefPtr<PermissionStatus> CreatePermissionStatus(
"a valid value for enumeration PermissionName.");
return nullptr;
}
return new PermissionStatus(aWindow, rootDesc.mName);
return new PermissionStatus(aGlobal, rootDesc.mName);
case PermissionName::Microphone:
if (!StaticPrefs::permissions_media_query_enabled()) {
aRv.ThrowTypeError(
@ -101,7 +102,7 @@ RefPtr<PermissionStatus> CreatePermissionStatus(
"not a valid value for enumeration PermissionName.");
return nullptr;
}
return new PermissionStatus(aWindow, rootDesc.mName);
return new PermissionStatus(aGlobal, rootDesc.mName);
default:
MOZ_ASSERT_UNREACHABLE("Unhandled type");
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
@ -118,23 +119,30 @@ already_AddRefed<Promise> Permissions::Query(JSContext* aCx,
// Step 1: If this's relevant global object is a Window object, then:
// Step 1.1: If the current settings object's associated Document is not fully
// active, return a promise rejected with an "InvalidStateError" DOMException.
//
// TODO(krosylight): The spec allows worker global while we don't, see bug
// 1193373.
if (!mWindow || !mWindow->IsFullyActive()) {
aRv.ThrowInvalidStateError("The document is not fully active.");
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
if (NS_WARN_IF(!global)) {
aRv.ThrowInvalidStateError("The context is not fully active.");
return nullptr;
}
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
if (!window || !window->IsFullyActive()) {
aRv.ThrowInvalidStateError("The document is not fully active.");
return nullptr;
}
}
// Step 2 - 6 and 8.1:
RefPtr<PermissionStatus> status =
CreatePermissionStatus(aCx, aPermission, mWindow, aRv);
CreatePermissionStatus(aCx, aPermission, global, aRv);
if (!status) {
return nullptr;
}
// Step 7: Let promise be a new promise.
RefPtr<Promise> promise = Promise::Create(mWindow->AsGlobal(), aRv);
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -143,7 +151,7 @@ already_AddRefed<Promise> Permissions::Query(JSContext* aCx,
// Step 8.4: Queue a global task on the permissions task source with this's
// relevant global object to resolve promise with status.
status->Init()->Then(
GetMainThreadSerialEventTarget(), __func__,
GetCurrentSerialEventTarget(), __func__,
[status, promise]() {
promise->MaybeResolve(status);
return;

View File

@ -8,8 +8,10 @@
#define mozilla_dom_Permissions_h_
#include "nsISupports.h"
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"
#include "mozilla/GlobalTeardownObserver.h"
class nsIGlobalObject;
namespace mozilla {
@ -21,14 +23,14 @@ class Promise;
class PermissionStatus;
struct PermissionSetParameters;
class Permissions final : public nsISupports, public nsWrapperCache {
class Permissions final : public GlobalTeardownObserver, public nsWrapperCache {
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Permissions)
explicit Permissions(nsPIDOMWindowInner* aWindow);
explicit Permissions(nsIGlobalObject* aGlobal);
nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
nsIGlobalObject* GetParentObject() const { return GetOwnerGlobal(); }
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -45,8 +47,6 @@ class Permissions final : public nsISupports, public nsWrapperCache {
private:
~Permissions();
nsCOMPtr<nsPIDOMWindowInner> mWindow;
};
} // namespace dom

View File

@ -12,62 +12,152 @@
#include "mozilla/dom/FeaturePolicyUtils.h"
#include "mozilla/dom/PermissionStatus.h"
#include "mozilla/dom/PermissionStatusBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRef.h"
#include "nsGlobalWindowInner.h"
#include "nsIPermissionManager.h"
#include "PermissionStatusSink.h"
namespace mozilla::dom {
class StorageAccessPermissionStatusSink final : public PermissionStatusSink {
Mutex mWorkerRefMutex;
// Protected by mutex.
// Created and released on worker-thread. Used also on main-thread.
RefPtr<WeakWorkerRef> mWeakWorkerRef MOZ_GUARDED_BY(mWorkerRefMutex);
public:
StorageAccessPermissionStatusSink(PermissionStatus* aPermissionStatus,
PermissionName aPermissionName,
const nsACString& aPermissionType)
: PermissionStatusSink(aPermissionStatus, aPermissionName,
aPermissionType),
mWorkerRefMutex("StorageAccessPermissionStatusSink::mWorkerRefMutex") {}
void Init() {
if (!NS_IsMainThread()) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
MutexAutoLock lock(mWorkerRefMutex);
mWeakWorkerRef =
WeakWorkerRef::Create(workerPrivate, [self = RefPtr(this)]() {
MutexAutoLock lock(self->mWorkerRefMutex);
self->mWeakWorkerRef = nullptr;
});
}
}
protected:
bool MaybeUpdatedByOnMainThread(nsIPermission* aPermission) override {
return false;
}
bool MaybeUpdatedByNotifyOnlyOnMainThread(
nsPIDOMWindowInner* aInnerWindow) override {
NS_ENSURE_TRUE(aInnerWindow, false);
if (!mPermissionStatus) {
return false;
}
nsCOMPtr<nsPIDOMWindowInner> ownerWindow;
if (mSerialEventTarget->IsOnCurrentThread()) {
ownerWindow = mPermissionStatus->GetOwnerWindow();
} else {
MutexAutoLock lock(mWorkerRefMutex);
if (!mWeakWorkerRef) {
return false;
}
// If we have mWeakWorkerRef, we haven't received the WorkerRef
// notification yet.
WorkerPrivate* workerPrivate = mWeakWorkerRef->GetUnsafePrivate();
MOZ_ASSERT(workerPrivate);
ownerWindow = workerPrivate->GetAncestorWindow();
}
NS_ENSURE_TRUE(ownerWindow, false);
return ownerWindow->WindowID() == aInnerWindow->WindowID();
}
RefPtr<PermissionStatePromise> ComputeStateOnMainThread() override {
if (mSerialEventTarget->IsOnCurrentThread()) {
if (!mPermissionStatus) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
}
nsGlobalWindowInner* window = mPermissionStatus->GetOwnerWindow();
if (NS_WARN_IF(!window)) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
}
WindowGlobalChild* wgc = window->GetWindowGlobalChild();
if (NS_WARN_IF(!wgc)) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
}
// Perform a Permission Policy Request
if (!FeaturePolicyUtils::IsFeatureAllowed(window->GetExtantDoc(),
u"storage-access"_ns)) {
return PermissionStatePromise::CreateAndResolve(
nsIPermissionManager::PROMPT_ACTION, __func__);
}
return wgc->SendGetStorageAccessPermission(false)->Then(
GetMainThreadSerialEventTarget(), __func__,
[self = RefPtr(this)](uint32_t aAction) {
// We never reveal PermissionState::Denied here
return PermissionStatePromise::CreateAndResolve(
aAction == nsIPermissionManager::ALLOW_ACTION
? aAction
: nsIPermissionManager::PROMPT_ACTION,
__func__);
},
[](mozilla::ipc::ResponseRejectReason aError) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
});
}
// For workers we already have the correct value in workerPrivate.
return InvokeAsync(mSerialEventTarget, __func__, [self = RefPtr(this)] {
if (!self->mPermissionStatus) {
return PermissionStatePromise::CreateAndReject(NS_ERROR_FAILURE,
__func__);
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
return PermissionStatePromise::CreateAndResolve(
workerPrivate->StorageAccess() == StorageAccess::eAllow
? nsIPermissionManager::ALLOW_ACTION
: nsIPermissionManager::PROMPT_ACTION,
__func__);
});
}
};
StorageAccessPermissionStatus::StorageAccessPermissionStatus(
nsPIDOMWindowInner* aWindow)
: PermissionStatus(aWindow, PermissionName::Storage_access) {}
nsIGlobalObject* aGlobal)
: PermissionStatus(aGlobal, PermissionName::Storage_access) {}
RefPtr<PermissionStatus::SimplePromise>
StorageAccessPermissionStatus::UpdateState() {
nsGlobalWindowInner* window = GetOwnerWindow();
if (NS_WARN_IF(!window)) {
return SimplePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
WindowGlobalChild* wgc = window->GetWindowGlobalChild();
if (NS_WARN_IF(!wgc)) {
return SimplePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// Perform a Permission Policy Request
if (!FeaturePolicyUtils::IsFeatureAllowed(window->GetExtantDoc(),
u"storage-access"_ns)) {
mState = PermissionState::Prompt;
return SimplePromise::CreateAndResolve(NS_OK, __func__);
}
RefPtr<StorageAccessPermissionStatus> self(this);
return wgc->SendGetStorageAccessPermission(false)->Then(
GetMainThreadSerialEventTarget(), __func__,
[self](uint32_t aAction) {
if (aAction == nsIPermissionManager::ALLOW_ACTION) {
self->mState = PermissionState::Granted;
} else {
// We never reveal PermissionState::Denied here
self->mState = PermissionState::Prompt;
}
return SimplePromise::CreateAndResolve(NS_OK, __func__);
},
[](mozilla::ipc::ResponseRejectReason aError) {
return SimplePromise::CreateAndResolve(NS_ERROR_FAILURE, __func__);
});
}
bool StorageAccessPermissionStatus::MaybeUpdatedBy(
nsIPermission* aPermission) const {
return false;
}
bool StorageAccessPermissionStatus::MaybeUpdatedByNotifyOnly(
nsPIDOMWindowInner* aInnerWindow) const {
nsPIDOMWindowInner* owner = GetOwnerWindow();
NS_ENSURE_TRUE(owner, false);
NS_ENSURE_TRUE(aInnerWindow, false);
return owner->WindowID() == aInnerWindow->WindowID();
already_AddRefed<PermissionStatusSink>
StorageAccessPermissionStatus::CreateSink() {
RefPtr<StorageAccessPermissionStatusSink> sink =
new StorageAccessPermissionStatusSink(this, Name(), GetPermissionType());
sink->Init();
return sink.forget();
}
} // namespace mozilla::dom

View File

@ -11,16 +11,15 @@
namespace mozilla::dom {
// The storage access permission from the Storage Access API has unique
// implementation details and should not be used as the basis for any other
// status sink implementations.
class StorageAccessPermissionStatus final : public PermissionStatus {
public:
explicit StorageAccessPermissionStatus(nsPIDOMWindowInner* aWindow);
explicit StorageAccessPermissionStatus(nsIGlobalObject* aGlobal);
private:
RefPtr<SimplePromise> UpdateState() override;
bool MaybeUpdatedBy(nsIPermission* aPermission) const override;
bool MaybeUpdatedByNotifyOnly(
nsPIDOMWindowInner* aInnerWindow) const override;
already_AddRefed<PermissionStatusSink> CreateSink() override;
};
} // namespace mozilla::dom

View File

@ -20,6 +20,7 @@ UNIFIED_SOURCES += [
"PermissionObserver.cpp",
"Permissions.cpp",
"PermissionStatus.cpp",
"PermissionStatusSink.cpp",
"PermissionUtils.cpp",
"StorageAccessPermissionStatus.cpp",
]

View File

@ -292,6 +292,10 @@ let interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceServerTiming",
// IMPORTANT: Do not change this list without review from a DOM peer!
"Permissions",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PermissionStatus",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ProgressEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"PromiseRejectionEvent",

View File

@ -106,8 +106,9 @@ interface mixin NavigatorStorageUtils {
//undefined yieldForStorageUpdates();
};
// https://w3c.github.io/permissions/#webidl-2112232240
partial interface Navigator {
[Throws]
[Throws, SameObject]
readonly attribute Permissions permissions;
};

View File

@ -13,7 +13,7 @@ enum PermissionState {
"prompt"
};
[Exposed=Window]
[Exposed=(Window,Worker)]
interface PermissionStatus : EventTarget {
readonly attribute PermissionName name;
readonly attribute PermissionState state;

View File

@ -33,7 +33,7 @@ dictionary MidiPermissionDescriptor : PermissionDescriptor {
// We don't implement `PushPermissionDescriptor` because we use a background
// message quota instead of `userVisibleOnly`.
[Exposed=Window]
[Exposed=(Window,Worker)]
interface Permissions {
[NewObject]
Promise<PermissionStatus> query(object permission);

View File

@ -34,3 +34,10 @@ WorkerNavigator includes NavigatorLocks;
// https://gpuweb.github.io/gpuweb/#navigator-gpu
WorkerNavigator includes NavigatorGPU;
// https://w3c.github.io/permissions/#webidl-2112232240
[Exposed=(Worker)]
partial interface WorkerNavigator {
[SameObject] readonly attribute Permissions permissions;
};

View File

@ -16,6 +16,7 @@
#include "mozilla/dom/LockManager.h"
#include "mozilla/dom/MediaCapabilities.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/dom/WorkerNavigatorBinding.h"
@ -50,6 +51,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkerNavigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaCapabilities)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebGpu)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
WorkerNavigator::WorkerNavigator(const NavigatorProperties& aProperties,
@ -87,6 +89,8 @@ void WorkerNavigator::Invalidate() {
mLocks->Shutdown();
mLocks = nullptr;
}
mPermissions = nullptr;
}
JSObject* WorkerNavigator::WrapObject(JSContext* aCx,
@ -286,4 +290,18 @@ dom::LockManager* WorkerNavigator::Locks() {
return mLocks;
}
dom::Permissions* WorkerNavigator::Permissions() {
if (!mPermissions) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
nsIGlobalObject* global = workerPrivate->GlobalScope();
MOZ_ASSERT(global);
mPermissions = new dom::Permissions(global);
}
return mPermissions;
}
} // namespace mozilla::dom

View File

@ -31,6 +31,7 @@ namespace dom {
class StorageManager;
class MediaCapabilities;
class LockManager;
class Permissions;
namespace network {
class Connection;
@ -46,6 +47,7 @@ class WorkerNavigator final : public nsWrapperCache {
RefPtr<dom::MediaCapabilities> mMediaCapabilities;
RefPtr<webgpu::Instance> mWebGpu;
RefPtr<dom::LockManager> mLocks;
RefPtr<dom::Permissions> mPermissions;
bool mOnline;
WorkerNavigator(const NavigatorProperties& aProperties, bool aOnline);
@ -113,6 +115,8 @@ class WorkerNavigator final : public nsWrapperCache {
webgpu::Instance* Gpu();
dom::LockManager* Locks();
dom::Permissions* Permissions();
};
} // namespace dom

View File

@ -20,6 +20,7 @@ var supportedProps = [
"hardwareConcurrency",
{ name: "storage", isSecureContext: true },
"connection",
"permissions",
];
self.onmessage = function (event) {

View File

@ -31,6 +31,7 @@ worker.onmessage = function (event) {
"gpu",
"locks",
"mediaCapabilities",
"permissions",
"storage",
];

View File

@ -302,6 +302,10 @@ let interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "PerformanceServerTiming", insecureContext: false },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "Permissions", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "PermissionStatus", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "ProgressEvent", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "PromiseRejectionEvent", insecureContext: true },

View File

@ -14336,7 +14336,7 @@
# Is support for Permissions.query enabled for camera and microphone?
- name: permissions.media.query.enabled
type: bool
type: RelaxedAtomicBool
value: true
mirror: always

View File

@ -1,84 +0,0 @@
[idlharness.any.worker.html]
[Permissions interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL
[PermissionStatus interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL
[Permissions interface: existence and properties of interface prototype object]
expected: FAIL
[Permissions interface: navigator.permissions must inherit property "query(object)" with the proper type]
expected: FAIL
[PermissionStatus interface: permissionStatus must inherit property "onchange" with the proper type]
expected: FAIL
[PermissionStatus interface: existence and properties of interface prototype object]
expected: FAIL
[Permissions interface object name]
expected: FAIL
[WorkerNavigator interface: attribute permissions]
expected: FAIL
[Permissions interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL
[WorkerNavigator interface: navigator must inherit property "permissions" with the proper type]
expected: FAIL
[Permissions interface object length]
expected: FAIL
[Permissions interface: operation query(object)]
expected: FAIL
[PermissionStatus interface object name]
expected: FAIL
[PermissionStatus interface: attribute state]
expected: FAIL
[PermissionStatus interface: existence and properties of interface object]
expected: FAIL
[Permissions must be primary interface of navigator.permissions]
expected: FAIL
[PermissionStatus interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL
[PermissionStatus interface: attribute onchange]
expected: FAIL
[PermissionStatus must be primary interface of permissionStatus]
expected: FAIL
[PermissionStatus interface object length]
expected: FAIL
[PermissionStatus interface: permissionStatus must inherit property "state" with the proper type]
expected: FAIL
[Permissions interface: existence and properties of interface object]
expected: FAIL
[Stringification of permissionStatus]
expected: FAIL
[Stringification of navigator.permissions]
expected: FAIL
[Permissions interface: calling query(object) on navigator.permissions with too few arguments must throw TypeError]
expected: FAIL
[PermissionStatus interface: attribute name]
expected: FAIL
[PermissionStatus interface: permissionStatus must inherit property "name" with the proper type]
expected: FAIL
[idlharness.any.html]

View File

@ -1,6 +0,0 @@
[permission-query.https.any.worker.html]
[The "persistent-storage" permission is recognized]
expected: FAIL
[permission-query.https.any.html]