Backed out changeset f5c51fd71f7b (bug 1193373) as requested by dmeehan for causing Bug 1923938. CLOSED TREE

This commit is contained in:
Stanca Serban 2024-10-10 22:03:50 +03:00
parent 8fbec65201
commit 00c679dc82
28 changed files with 326 additions and 618 deletions

View File

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

View File

@ -11,9 +11,9 @@
namespace mozilla::dom {
MidiPermissionStatus::MidiPermissionStatus(nsIGlobalObject* aGlobal,
MidiPermissionStatus::MidiPermissionStatus(nsPIDOMWindowInner* aWindow,
bool aSysex)
: PermissionStatus(aGlobal, PermissionName::Midi), mSysex(aSysex) {}
: PermissionStatus(aWindow, 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(nsIGlobalObject* aGlobal, bool aSysex);
MidiPermissionStatus(nsPIDOMWindowInner* aWindow, bool aSysex);
private:
~MidiPermissionStatus() {}

View File

@ -6,11 +6,12 @@
#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 {
@ -21,13 +22,9 @@ PermissionObserver* gInstance = nullptr;
NS_IMPL_ISUPPORTS(PermissionObserver, nsIObserver, nsISupportsWeakReference)
PermissionObserver::PermissionObserver() {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
MOZ_ASSERT(!gInstance);
}
PermissionObserver::PermissionObserver() { MOZ_ASSERT(!gInstance); }
PermissionObserver::~PermissionObserver() {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
MOZ_ASSERT(mSinks.IsEmpty());
MOZ_ASSERT(gInstance == this);
@ -36,8 +33,6 @@ PermissionObserver::~PermissionObserver() {
/* static */
already_AddRefed<PermissionObserver> PermissionObserver::GetInstance() {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
RefPtr<PermissionObserver> instance = gInstance;
if (!instance) {
instance = new PermissionObserver();
@ -63,16 +58,14 @@ already_AddRefed<PermissionObserver> PermissionObserver::GetInstance() {
return instance.forget();
}
void PermissionObserver::AddSink(PermissionStatusSink* aSink) {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
void PermissionObserver::AddSink(PermissionStatus* aSink) {
MOZ_ASSERT(aSink);
MOZ_ASSERT(!mSinks.Contains(aSink));
mSinks.AppendElement(aSink);
}
void PermissionObserver::RemoveSink(PermissionStatusSink* aSink) {
MOZ_ASSERT_DEBUG_OR_FUZZING(NS_IsMainThread());
void PermissionObserver::RemoveSink(PermissionStatus* aSink) {
MOZ_ASSERT(aSink);
MOZ_ASSERT(mSinks.Contains(aSink));
@ -82,7 +75,6 @@ void PermissionObserver::RemoveSink(PermissionStatusSink* 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"));
@ -110,15 +102,15 @@ PermissionObserver::Observe(nsISupports* aSubject, const char* aTopic,
Maybe<PermissionName> permission = TypeToPermissionName(type);
if (permission) {
for (PermissionStatusSink* sink : mSinks) {
if (sink->Name() != permission.value()) {
for (auto* sink : mSinks) {
if (sink->mName != 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->MaybeUpdatedByOnMainThread(perm)) {
sink->PermissionChangedOnMainThread();
if (perm && sink->MaybeUpdatedBy(perm)) {
sink->PermissionChanged();
}
// Check for permissions that are changed for this sink's principal
// via the "perm-changed-notify-only" notification. These permissions
@ -127,9 +119,8 @@ 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->MaybeUpdatedByNotifyOnlyOnMainThread(innerWindow)) {
sink->PermissionChangedOnMainThread();
if (innerWindow && sink->MaybeUpdatedByNotifyOnly(innerWindow)) {
sink->PermissionChanged();
}
}
}

View File

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

View File

@ -5,20 +5,21 @@
* 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(nsIGlobalObject* aGlobal,
PermissionStatus::PermissionStatus(nsPIDOMWindowInner* aWindow,
PermissionName aName)
: DOMEventTargetHelper(aGlobal),
: DOMEventTargetHelper(aWindow),
mName(aName),
mState(PermissionState::Denied) {
KeepAliveIfHasListenersFor(nsGkAtoms::onchange);
@ -27,26 +28,24 @@ PermissionStatus::PermissionStatus(nsIGlobalObject* aGlobal,
// https://w3c.github.io/permissions/#onchange-attribute and
// https://w3c.github.io/permissions/#query-method
RefPtr<PermissionStatus::SimplePromise> PermissionStatus::Init() {
mSink = CreateSink();
MOZ_ASSERT(mSink);
// 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__);
}
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__);
}
mObserver->AddSink(this);
return SimplePromise::CreateAndReject(aResult.RejectValue(), __func__);
});
// Covers the query part (Step 8.2 - 8.4)
return UpdateState();
}
PermissionStatus::~PermissionStatus() {
if (mSink) {
mSink->Disentangle();
mSink = nullptr;
if (mObserver) {
mObserver->RemoveSink(this);
}
}
@ -59,28 +58,111 @@ nsLiteralCString PermissionStatus::GetPermissionType() const {
return PermissionNameToType(mName);
}
// https://w3c.github.io/permissions/#dfn-permissionstatus-update-steps
void PermissionStatus::PermissionChanged(uint32_t aAction) {
PermissionState newState = ComputeStateFromAction(aAction);
if (mState == newState) {
return;
// 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__);
}
mState = newState;
RefPtr<Document> document = window->GetExtantDoc();
if (NS_WARN_IF(!document)) {
return SimplePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
// 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();
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.
// 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();
}
},
[]() {
});
}
void PermissionStatus::DisconnectFromOwner() {
IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onchange);
if (mSink) {
mSink->Disentangle();
mSink = nullptr;
if (mObserver) {
mObserver->RemoveSink(this);
mObserver = nullptr;
}
DOMEventTargetHelper::DisconnectFromOwner();
@ -90,19 +172,4 @@ 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,18 +11,19 @@
#include "mozilla/dom/PermissionStatusBinding.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/MozPromise.h"
#include "nsIPermission.h"
namespace mozilla::dom {
class PermissionStatusSink;
class PermissionObserver;
class PermissionStatus : public DOMEventTargetHelper {
friend class PermissionStatusSink;
friend class PermissionObserver;
public:
using SimplePromise = MozPromise<nsresult, nsresult, true>;
PermissionStatus(nsIGlobalObject* aGlobal, PermissionName aName);
PermissionStatus(nsPIDOMWindowInner* aWindow, PermissionName aName);
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -56,14 +57,23 @@ class PermissionStatus : public DOMEventTargetHelper {
virtual nsLiteralCString GetPermissionType() const;
private:
virtual already_AddRefed<PermissionStatusSink> CreateSink();
virtual RefPtr<SimplePromise> UpdateState();
void PermissionChanged(uint32_t aAction);
// 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;
PermissionState ComputeStateFromAction(uint32_t aAction);
void PermissionChanged();
PermissionName mName;
RefPtr<PermissionStatusSink> mSink;
RefPtr<PermissionObserver> mObserver;
protected:
PermissionState mState;

View File

@ -1,231 +0,0 @@
/* -*- 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

@ -1,81 +0,0 @@
/* -*- 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,9 +60,7 @@ Maybe<PermissionName> TypeToPermissionName(const nsACString& aType) {
}
PermissionState ActionToPermissionState(uint32_t aAction, PermissionName aName,
nsIGlobalObject* aGlobal) {
MOZ_ASSERT(aGlobal);
const Document& aDocument) {
switch (aAction) {
case nsIPermissionManager::ALLOW_ACTION:
return PermissionState::Granted;
@ -73,7 +71,7 @@ PermissionState ActionToPermissionState(uint32_t aAction, PermissionName aName,
case nsIPermissionManager::PROMPT_ACTION:
if ((aName == PermissionName::Camera ||
aName == PermissionName::Microphone) &&
!aGlobal->ShouldResistFingerprinting(RFPTarget::MediaDevices)) {
!aDocument.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,7 +9,6 @@
#include "mozilla/dom/PermissionsBinding.h"
#include "mozilla/dom/PermissionStatusBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/Maybe.h"
namespace mozilla::dom {
@ -28,7 +27,7 @@ const nsLiteralCString& PermissionNameToType(PermissionName aName);
Maybe<PermissionName> TypeToPermissionName(const nsACString& aType);
PermissionState ActionToPermissionState(uint32_t aAction, PermissionName aName,
nsIGlobalObject* aGlobal);
const Document& aDocument);
} // namespace mozilla::dom

View File

@ -27,10 +27,9 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Permissions)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Permissions)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Permissions)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Permissions, mWindow)
Permissions::Permissions(nsIGlobalObject* aGlobal)
: GlobalTeardownObserver(aGlobal) {}
Permissions::Permissions(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
Permissions::~Permissions() = default;
@ -46,7 +45,7 @@ namespace {
// commands
RefPtr<PermissionStatus> CreatePermissionStatus(
JSContext* aCx, JS::Handle<JSObject*> aPermissionDesc,
nsIGlobalObject* aGlobal, ErrorResult& aRv) {
nsPIDOMWindowInner* aWindow, ErrorResult& aRv) {
// Step 2: Let rootDesc be the object permissionDesc refers to, converted to
// an IDL value of type PermissionDescriptor.
PermissionDescriptor rootDesc;
@ -77,16 +76,16 @@ RefPtr<PermissionStatus> CreatePermissionStatus(
return nullptr;
}
return new MidiPermissionStatus(aGlobal, midiPerm.mSysex);
return new MidiPermissionStatus(aWindow, midiPerm.mSysex);
}
case PermissionName::Storage_access:
return new StorageAccessPermissionStatus(aGlobal);
return new StorageAccessPermissionStatus(aWindow);
case PermissionName::Geolocation:
case PermissionName::Notifications:
case PermissionName::Push:
case PermissionName::Persistent_storage:
case PermissionName::Screen_wake_lock:
return new PermissionStatus(aGlobal, rootDesc.mName);
return new PermissionStatus(aWindow, rootDesc.mName);
case PermissionName::Camera:
if (!StaticPrefs::permissions_media_query_enabled()) {
aRv.ThrowTypeError(
@ -94,7 +93,7 @@ RefPtr<PermissionStatus> CreatePermissionStatus(
"a valid value for enumeration PermissionName.");
return nullptr;
}
return new PermissionStatus(aGlobal, rootDesc.mName);
return new PermissionStatus(aWindow, rootDesc.mName);
case PermissionName::Microphone:
if (!StaticPrefs::permissions_media_query_enabled()) {
aRv.ThrowTypeError(
@ -102,7 +101,7 @@ RefPtr<PermissionStatus> CreatePermissionStatus(
"not a valid value for enumeration PermissionName.");
return nullptr;
}
return new PermissionStatus(aGlobal, rootDesc.mName);
return new PermissionStatus(aWindow, rootDesc.mName);
default:
MOZ_ASSERT_UNREACHABLE("Unhandled type");
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
@ -119,30 +118,23 @@ 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.
nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
if (NS_WARN_IF(!global)) {
aRv.ThrowInvalidStateError("The context is not fully active.");
//
// 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.");
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, global, aRv);
CreatePermissionStatus(aCx, aPermission, mWindow, aRv);
if (!status) {
return nullptr;
}
// Step 7: Let promise be a new promise.
RefPtr<Promise> promise = Promise::Create(global, aRv);
RefPtr<Promise> promise = Promise::Create(mWindow->AsGlobal(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -151,7 +143,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(
GetCurrentSerialEventTarget(), __func__,
GetMainThreadSerialEventTarget(), __func__,
[status, promise]() {
promise->MaybeResolve(status);
return;

View File

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

View File

@ -12,152 +12,62 @@
#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(
nsIGlobalObject* aGlobal)
: PermissionStatus(aGlobal, PermissionName::Storage_access) {}
nsPIDOMWindowInner* aWindow)
: PermissionStatus(aWindow, PermissionName::Storage_access) {}
already_AddRefed<PermissionStatusSink>
StorageAccessPermissionStatus::CreateSink() {
RefPtr<StorageAccessPermissionStatusSink> sink =
new StorageAccessPermissionStatusSink(this, Name(), GetPermissionType());
sink->Init();
return sink.forget();
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();
}
} // namespace mozilla::dom

View File

@ -11,15 +11,16 @@
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(nsIGlobalObject* aGlobal);
explicit StorageAccessPermissionStatus(nsPIDOMWindowInner* aWindow);
private:
already_AddRefed<PermissionStatusSink> CreateSink() override;
RefPtr<SimplePromise> UpdateState() override;
bool MaybeUpdatedBy(nsIPermission* aPermission) const override;
bool MaybeUpdatedByNotifyOnly(
nsPIDOMWindowInner* aInnerWindow) const override;
};
} // namespace mozilla::dom

View File

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

View File

@ -292,10 +292,6 @@ 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,9 +106,8 @@ interface mixin NavigatorStorageUtils {
//undefined yieldForStorageUpdates();
};
// https://w3c.github.io/permissions/#webidl-2112232240
partial interface Navigator {
[Throws, SameObject]
[Throws]
readonly attribute Permissions permissions;
};

View File

@ -13,7 +13,7 @@ enum PermissionState {
"prompt"
};
[Exposed=(Window,Worker)]
[Exposed=Window]
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,Worker)]
[Exposed=Window]
interface Permissions {
[NewObject]
Promise<PermissionStatus> query(object permission);

View File

@ -34,10 +34,3 @@ 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,7 +16,6 @@
#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"
@ -51,7 +50,6 @@ 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,
@ -89,8 +87,6 @@ void WorkerNavigator::Invalidate() {
mLocks->Shutdown();
mLocks = nullptr;
}
mPermissions = nullptr;
}
JSObject* WorkerNavigator::WrapObject(JSContext* aCx,
@ -290,18 +286,4 @@ 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,7 +31,6 @@ namespace dom {
class StorageManager;
class MediaCapabilities;
class LockManager;
class Permissions;
namespace network {
class Connection;
@ -47,7 +46,6 @@ 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);
@ -115,8 +113,6 @@ class WorkerNavigator final : public nsWrapperCache {
webgpu::Instance* Gpu();
dom::LockManager* Locks();
dom::Permissions* Permissions();
};
} // namespace dom

View File

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

View File

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

View File

@ -302,10 +302,6 @@ 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

@ -0,0 +1,84 @@
[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

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