1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/Permissions.h"
9 #include "mozilla/dom/Document.h"
10 #include "mozilla/dom/MidiPermissionStatus.h"
11 #include "mozilla/dom/PermissionSetParametersBinding.h"
12 #include "mozilla/dom/PermissionStatus.h"
13 #include "mozilla/dom/PermissionsBinding.h"
14 #include "mozilla/dom/Promise.h"
15 #include "mozilla/dom/RootedDictionary.h"
16 #include "mozilla/dom/StorageAccessPermissionStatus.h"
17 #include "mozilla/StaticPrefs_permissions.h"
18 #include "PermissionUtils.h"
20 namespace mozilla::dom
{
22 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Permissions
)
23 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
24 NS_INTERFACE_MAP_ENTRY(nsISupports
)
27 NS_IMPL_CYCLE_COLLECTING_ADDREF(Permissions
)
28 NS_IMPL_CYCLE_COLLECTING_RELEASE(Permissions
)
30 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Permissions
)
32 Permissions::Permissions(nsIGlobalObject
* aGlobal
)
33 : GlobalTeardownObserver(aGlobal
) {}
35 Permissions::~Permissions() = default;
37 JSObject
* Permissions::WrapObject(JSContext
* aCx
,
38 JS::Handle
<JSObject
*> aGivenProto
) {
39 return Permissions_Binding::Wrap(aCx
, this, aGivenProto
);
44 // Steps to parse PermissionDescriptor in
45 // https://w3c.github.io/permissions/#query-method and relevant WebDriver
47 RefPtr
<PermissionStatus
> CreatePermissionStatus(
48 JSContext
* aCx
, JS::Handle
<JSObject
*> aPermissionDesc
,
49 nsIGlobalObject
* aGlobal
, ErrorResult
& aRv
) {
50 // Step 2: Let rootDesc be the object permissionDesc refers to, converted to
51 // an IDL value of type PermissionDescriptor.
52 PermissionDescriptor rootDesc
;
53 JS::Rooted
<JS::Value
> permissionDescValue(
54 aCx
, JS::ObjectOrNullValue(aPermissionDesc
));
55 if (NS_WARN_IF(!rootDesc
.Init(aCx
, permissionDescValue
))) {
56 // Step 3: If the conversion throws an exception, return a promise rejected
57 // with that exception.
58 // Step 4: If rootDesc["name"] is not supported, return a promise rejected
59 // with a TypeError. (This is done by `enum PermissionName`, as the spec
60 // note says: "implementers are encouraged to use their own custom enum
62 aRv
.NoteJSContextException(aCx
);
66 // Step 5: Let typedDescriptor be the object permissionDesc refers to,
67 // converted to an IDL value of rootDesc's name's permission descriptor type.
68 // Step 6: If the conversion throws an exception, return a promise rejected
69 // with that exception.
70 // Step 8.1: Let status be create a PermissionStatus with typedDescriptor.
71 // (The rest is done by the caller)
72 switch (rootDesc
.mName
) {
73 case PermissionName::Midi
: {
74 MidiPermissionDescriptor midiPerm
;
75 if (NS_WARN_IF(!midiPerm
.Init(aCx
, permissionDescValue
))) {
76 aRv
.NoteJSContextException(aCx
);
80 return new MidiPermissionStatus(aGlobal
, midiPerm
.mSysex
);
82 case PermissionName::Storage_access
:
83 return new StorageAccessPermissionStatus(aGlobal
);
84 case PermissionName::Geolocation
:
85 case PermissionName::Notifications
:
86 case PermissionName::Push
:
87 case PermissionName::Persistent_storage
:
88 case PermissionName::Screen_wake_lock
:
89 return new PermissionStatus(aGlobal
, rootDesc
.mName
);
90 case PermissionName::Camera
:
91 if (!StaticPrefs::permissions_media_query_enabled()) {
93 "'camera' (value of 'name' member of PermissionDescriptor) is not "
94 "a valid value for enumeration PermissionName.");
97 return new PermissionStatus(aGlobal
, rootDesc
.mName
);
98 case PermissionName::Microphone
:
99 if (!StaticPrefs::permissions_media_query_enabled()) {
101 "'microphone' (value of 'name' member of PermissionDescriptor) is "
102 "not a valid value for enumeration PermissionName.");
105 return new PermissionStatus(aGlobal
, rootDesc
.mName
);
107 MOZ_ASSERT_UNREACHABLE("Unhandled type");
108 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
115 // https://w3c.github.io/permissions/#query-method
116 already_AddRefed
<Promise
> Permissions::Query(JSContext
* aCx
,
117 JS::Handle
<JSObject
*> aPermission
,
119 // Step 1: If this's relevant global object is a Window object, then:
120 // Step 1.1: If the current settings object's associated Document is not fully
121 // active, return a promise rejected with an "InvalidStateError" DOMException.
123 nsCOMPtr
<nsIGlobalObject
> global
= GetOwnerGlobal();
124 if (NS_WARN_IF(!global
)) {
125 aRv
.ThrowInvalidStateError("The context is not fully active.");
129 if (NS_IsMainThread()) {
130 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(global
);
131 if (!window
|| !window
->IsFullyActive()) {
132 aRv
.ThrowInvalidStateError("The document is not fully active.");
137 // Step 2 - 6 and 8.1:
138 RefPtr
<PermissionStatus
> status
=
139 CreatePermissionStatus(aCx
, aPermission
, global
, aRv
);
144 // Step 7: Let promise be a new promise.
145 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
146 if (NS_WARN_IF(aRv
.Failed())) {
150 // Step 8.2 - 8.3: (Done by the Init method)
151 // Step 8.4: Queue a global task on the permissions task source with this's
152 // relevant global object to resolve promise with status.
153 status
->Init()->Then(
154 GetCurrentSerialEventTarget(), __func__
,
155 [status
, promise
]() {
156 promise
->MaybeResolve(status
);
159 [promise
](nsresult aError
) {
160 MOZ_ASSERT(NS_FAILED(aError
));
161 NS_WARNING("Failed PermissionStatus creation");
162 promise
->MaybeReject(aError
);
166 return promise
.forget();
169 already_AddRefed
<PermissionStatus
> Permissions::ParseSetParameters(
170 JSContext
* aCx
, const PermissionSetParameters
& aParameters
,
172 // Step 1: Let parametersDict be the parameters argument, converted to an IDL
173 // value of type PermissionSetParameters. If this throws an exception,
174 // return an invalid argument error.
175 // (Done by IDL layer, and the error type should be handled by the caller)
177 // Step 2: If parametersDict.state is an inappropriate permission state for
178 // any implementation-defined reason, return a invalid argument error.
179 // (We don't do this)
181 // Step 3: Let rootDesc be parametersDict.descriptor.
182 JS::Rooted
<JSObject
*> rootDesc(aCx
, aParameters
.mDescriptor
);
184 // Step 4: Let typedDescriptor be the object rootDesc refers to, converted
185 // to an IDL value of rootDesc.name's permission descriptor type. If this
186 // throws an exception, return a invalid argument error.
188 // We use PermissionStatus as the typed object.
189 RefPtr
<PermissionStatus
> status
=
190 CreatePermissionStatus(aCx
, rootDesc
, nullptr, aRv
);
195 // Set the state too so that the caller can use it for step 5.
196 status
->SetState(aParameters
.mState
);
198 return status
.forget();
201 } // namespace mozilla::dom