mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
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:
parent
c857667f85
commit
8ae9152b88
@ -516,7 +516,7 @@ Permissions* Navigator::GetPermissions(ErrorResult& aRv) {
|
||||
}
|
||||
|
||||
if (!mPermissions) {
|
||||
mPermissions = new Permissions(mWindow);
|
||||
mPermissions = new Permissions(mWindow->AsGlobal());
|
||||
}
|
||||
|
||||
return mPermissions;
|
||||
|
@ -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;
|
||||
|
@ -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() {}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
231
dom/permission/PermissionStatusSink.cpp
Normal file
231
dom/permission/PermissionStatusSink.cpp
Normal 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
|
81
dom/permission/PermissionStatusSink.h
Normal file
81
dom/permission/PermissionStatusSink.h
Normal 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
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -20,6 +20,7 @@ UNIFIED_SOURCES += [
|
||||
"PermissionObserver.cpp",
|
||||
"Permissions.cpp",
|
||||
"PermissionStatus.cpp",
|
||||
"PermissionStatusSink.cpp",
|
||||
"PermissionUtils.cpp",
|
||||
"StorageAccessPermissionStatus.cpp",
|
||||
]
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,7 @@ enum PermissionState {
|
||||
"prompt"
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
[Exposed=(Window,Worker)]
|
||||
interface PermissionStatus : EventTarget {
|
||||
readonly attribute PermissionName name;
|
||||
readonly attribute PermissionState state;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -20,6 +20,7 @@ var supportedProps = [
|
||||
"hardwareConcurrency",
|
||||
{ name: "storage", isSecureContext: true },
|
||||
"connection",
|
||||
"permissions",
|
||||
];
|
||||
|
||||
self.onmessage = function (event) {
|
||||
|
@ -31,6 +31,7 @@ worker.onmessage = function (event) {
|
||||
"gpu",
|
||||
"locks",
|
||||
"mediaCapabilities",
|
||||
"permissions",
|
||||
"storage",
|
||||
];
|
||||
|
||||
|
@ -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 },
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]
|
@ -1,6 +0,0 @@
|
||||
[permission-query.https.any.worker.html]
|
||||
[The "persistent-storage" permission is recognized]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[permission-query.https.any.html]
|
Loading…
Reference in New Issue
Block a user