Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / permission / Permissions.cpp
blobc51e1bb0f29a4e04b8c834cd7b5dfcc5beb5b98f
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)
25 NS_INTERFACE_MAP_END
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);
42 namespace {
44 // Steps to parse PermissionDescriptor in
45 // https://w3c.github.io/permissions/#query-method and relevant WebDriver
46 // commands
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
61 // here")
62 aRv.NoteJSContextException(aCx);
63 return nullptr;
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);
77 return nullptr;
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()) {
92 aRv.ThrowTypeError(
93 "'camera' (value of 'name' member of PermissionDescriptor) is not "
94 "a valid value for enumeration PermissionName.");
95 return nullptr;
97 return new PermissionStatus(aGlobal, rootDesc.mName);
98 case PermissionName::Microphone:
99 if (!StaticPrefs::permissions_media_query_enabled()) {
100 aRv.ThrowTypeError(
101 "'microphone' (value of 'name' member of PermissionDescriptor) is "
102 "not a valid value for enumeration PermissionName.");
103 return nullptr;
105 return new PermissionStatus(aGlobal, rootDesc.mName);
106 default:
107 MOZ_ASSERT_UNREACHABLE("Unhandled type");
108 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
109 return nullptr;
113 } // namespace
115 // https://w3c.github.io/permissions/#query-method
116 already_AddRefed<Promise> Permissions::Query(JSContext* aCx,
117 JS::Handle<JSObject*> aPermission,
118 ErrorResult& aRv) {
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.");
126 return nullptr;
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.");
133 return nullptr;
137 // Step 2 - 6 and 8.1:
138 RefPtr<PermissionStatus> status =
139 CreatePermissionStatus(aCx, aPermission, global, aRv);
140 if (!status) {
141 return nullptr;
144 // Step 7: Let promise be a new promise.
145 RefPtr<Promise> promise = Promise::Create(global, aRv);
146 if (NS_WARN_IF(aRv.Failed())) {
147 return nullptr;
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);
157 return;
159 [promise](nsresult aError) {
160 MOZ_ASSERT(NS_FAILED(aError));
161 NS_WARNING("Failed PermissionStatus creation");
162 promise->MaybeReject(aError);
163 return;
166 return promise.forget();
169 already_AddRefed<PermissionStatus> Permissions::ParseSetParameters(
170 JSContext* aCx, const PermissionSetParameters& aParameters,
171 ErrorResult& aRv) {
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);
191 if (aRv.Failed()) {
192 return nullptr;
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