1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use embedder_traits::{self, AllowOrDeny, EmbedderMsg, PermissionFeature, WakeLockType};
9use js::context::JSContext;
10use js::conversions::ConversionResult;
11use js::jsapi::JSObject;
12use js::jsval::{ObjectValue, UndefinedValue};
13use js::realm::CurrentRealm;
14use script_bindings::inheritance::Castable;
15use script_bindings::reflector::{Reflector, reflect_dom_object};
16use servo_base::generic_channel;
17use servo_config::pref;
18
19use crate::conversions::Convert;
20use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{
21 PermissionDescriptor, PermissionName, PermissionState, PermissionStatusMethods,
22};
23use crate::dom::bindings::codegen::Bindings::PermissionsBinding::PermissionsMethods;
24use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
25use crate::dom::bindings::error::Error;
26use crate::dom::bindings::reflector::DomGlobal;
27use crate::dom::bindings::root::DomRoot;
28#[cfg(feature = "bluetooth")]
29use crate::dom::bluetooth::Bluetooth;
30#[cfg(feature = "bluetooth")]
31use crate::dom::bluetoothpermissionresult::BluetoothPermissionResult;
32use crate::dom::globalscope::GlobalScope;
33use crate::dom::permissionstatus::PermissionStatus;
34use crate::dom::promise::Promise;
35use crate::dom::window::Window;
36use crate::script_runtime::CanGc;
37
38pub(crate) trait PermissionAlgorithm {
39 type Descriptor;
40 #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
41 type Status;
42 fn create_descriptor(
43 cx: &mut JSContext,
44 permission_descriptor_obj: *mut JSObject,
45 ) -> Result<Self::Descriptor, Error>;
46 fn permission_query(
47 cx: &mut JSContext,
48 promise: &Rc<Promise>,
49 descriptor: &Self::Descriptor,
50 status: &Self::Status,
51 );
52 fn permission_request(
53 cx: &mut JSContext,
54 promise: &Rc<Promise>,
55 descriptor: &Self::Descriptor,
56 status: &Self::Status,
57 );
58 fn permission_revoke(cx: &mut JSContext, descriptor: &Self::Descriptor, status: &Self::Status);
59}
60
61enum Operation {
62 Query,
63 Request,
64 Revoke,
65}
66
67#[dom_struct]
69pub(crate) struct Permissions {
70 reflector_: Reflector,
71}
72
73impl Permissions {
74 pub(crate) fn new_inherited() -> Permissions {
75 Permissions {
76 reflector_: Reflector::new(),
77 }
78 }
79
80 pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<Permissions> {
81 reflect_dom_object(Box::new(Permissions::new_inherited()), global, can_gc)
82 }
83
84 fn manipulate(
88 &self,
89 cx: &mut CurrentRealm,
90 op: Operation,
91 permission_desc: *mut JSObject,
92 promise: Option<Rc<Promise>>,
93 ) -> Rc<Promise> {
94 let p = match promise {
96 Some(promise) => promise,
97 None => Promise::new_in_realm(cx),
98 };
99
100 let root_desc = match Permissions::create_descriptor(cx, permission_desc) {
102 Ok(descriptor) => descriptor,
103 Err(error) => {
104 p.reject_error(cx, error);
105 return p;
106 },
107 };
108
109 let status = PermissionStatus::new(&self.global(), &root_desc, CanGc::from_cx(cx));
111
112 match root_desc.name {
114 #[cfg(feature = "bluetooth")]
115 PermissionName::Bluetooth => {
116 let bluetooth_desc = match Bluetooth::create_descriptor(cx, permission_desc) {
117 Ok(descriptor) => descriptor,
118 Err(error) => {
119 p.reject_error(cx, error);
120 return p;
121 },
122 };
123
124 let result = BluetoothPermissionResult::new(cx, &self.global(), &status);
126
127 match op {
128 Operation::Request => {
130 Bluetooth::permission_request(cx, &p, &bluetooth_desc, &result)
131 },
132
133 Operation::Query => {
135 Bluetooth::permission_query(cx, &p, &bluetooth_desc, &result)
136 },
137
138 Operation::Revoke => {
139 let globalscope = self.global();
141 globalscope
142 .permission_state_invocation_results()
143 .borrow_mut()
144 .remove(&root_desc.name);
145
146 Bluetooth::permission_revoke(cx, &bluetooth_desc, &result)
148 },
149 }
150 },
151 _ => {
152 match op {
153 Operation::Request => {
154 Permissions::permission_request(cx, &p, &root_desc, &status);
156
157 p.resolve_native(cx, &status);
161 },
162 Operation::Query => {
163 Permissions::permission_query(cx, &p, &root_desc, &status);
165
166 p.resolve_native(cx, &status);
168 },
169
170 Operation::Revoke => {
171 let globalscope = self.global();
173 globalscope
174 .permission_state_invocation_results()
175 .borrow_mut()
176 .remove(&root_desc.name);
177
178 Permissions::permission_revoke(cx, &root_desc, &status);
180 },
181 }
182 },
183 };
184 match op {
185 Operation::Revoke => self.manipulate(cx, Operation::Query, permission_desc, Some(p)),
187
188 _ => p,
190 }
191 }
192}
193
194impl PermissionsMethods<crate::DomTypeHolder> for Permissions {
195 fn Query(&self, cx: &mut CurrentRealm, permission_desc: *mut JSObject) -> Rc<Promise> {
197 self.manipulate(cx, Operation::Query, permission_desc, None)
198 }
199
200 fn Request(&self, cx: &mut CurrentRealm, permission_desc: *mut JSObject) -> Rc<Promise> {
202 self.manipulate(cx, Operation::Request, permission_desc, None)
203 }
204
205 fn Revoke(&self, cx: &mut CurrentRealm, permission_desc: *mut JSObject) -> Rc<Promise> {
207 self.manipulate(cx, Operation::Revoke, permission_desc, None)
208 }
209}
210
211impl PermissionAlgorithm for Permissions {
212 type Descriptor = PermissionDescriptor;
213 type Status = PermissionStatus;
214
215 fn create_descriptor(
216 cx: &mut JSContext,
217 permission_descriptor_obj: *mut JSObject,
218 ) -> Result<PermissionDescriptor, Error> {
219 rooted!(&in(cx) let mut property = UndefinedValue());
220 property
221 .handle_mut()
222 .set(ObjectValue(permission_descriptor_obj));
223 match PermissionDescriptor::new(cx, property.handle()) {
224 Ok(ConversionResult::Success(descriptor)) => Ok(descriptor),
225 Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into_owned())),
226 Err(_) => Err(Error::JSFailed),
227 }
228 }
229
230 fn permission_query(
242 _cx: &mut JSContext,
243 _promise: &Rc<Promise>,
244 _descriptor: &PermissionDescriptor,
245 status: &PermissionStatus,
246 ) {
247 status.set_state(descriptor_permission_state(status.get_query(), None));
249 }
250
251 fn permission_request(
253 cx: &mut JSContext,
254 promise: &Rc<Promise>,
255 descriptor: &PermissionDescriptor,
256 status: &PermissionStatus,
257 ) {
258 Permissions::permission_query(cx, promise, descriptor, status);
260
261 match status.State() {
262 PermissionState::Prompt => {
264 let permission_name = status.get_query();
265 let globalscope = GlobalScope::current().expect("No current global object");
266 request_permission_to_use(permission_name, &globalscope);
267 },
268
269 _ => return,
271 }
272
273 Permissions::permission_query(cx, promise, descriptor, status);
275 }
276
277 fn permission_revoke(
278 _cx: &mut JSContext,
279 _descriptor: &PermissionDescriptor,
280 _status: &PermissionStatus,
281 ) {
282 }
283}
284
285pub(crate) fn descriptor_permission_state(
287 feature: PermissionName,
288 env_settings_obj: Option<&GlobalScope>,
289) -> PermissionState {
290 let global_scope = match env_settings_obj {
292 Some(env_settings_obj) => DomRoot::from_ref(env_settings_obj),
293 None => GlobalScope::current().expect("No current global object"),
294 };
295
296 if !global_scope.is_secure_context() {
298 if pref!(dom_permissions_testing_allowed_in_nonsecure_contexts) {
299 return PermissionState::Granted;
300 }
301 return PermissionState::Denied;
302 }
303
304 if let Some(window) = global_scope.downcast::<Window>() &&
312 !window.Document().allowed_to_use_feature(feature)
313 {
314 return PermissionState::Denied;
315 }
316
317 if let Some(entry) = global_scope
324 .permission_state_invocation_results()
325 .borrow()
326 .get(&feature)
327 {
328 return *entry;
329 }
330
331 PermissionState::Prompt
335}
336
337pub(crate) fn request_permission_to_use(
339 name: PermissionName,
340 global_scope: &GlobalScope,
341) -> PermissionState {
342 let state = descriptor_permission_state(name, Some(global_scope));
343 if state != PermissionState::Prompt {
344 return state;
345 }
346
347 let state = prompt_user_from_embedder(name, global_scope);
348 global_scope
349 .permission_state_invocation_results()
350 .borrow_mut()
351 .insert(name, state);
352 descriptor_permission_state(name, Some(global_scope))
353}
354
355fn prompt_user_from_embedder(name: PermissionName, global_scope: &GlobalScope) -> PermissionState {
356 let Some(webview_id) = global_scope.webview_id() else {
357 warn!("Requesting permissions from non-webview-associated global scope");
358 return PermissionState::Denied;
359 };
360 let (sender, receiver) = generic_channel::channel().expect("Failed to create IPC channel!");
361 global_scope.send_to_embedder(EmbedderMsg::PromptPermission(
362 webview_id,
363 name.convert(),
364 sender,
365 ));
366
367 match receiver.recv() {
368 Ok(AllowOrDeny::Allow) => PermissionState::Granted,
369 Ok(AllowOrDeny::Deny) => PermissionState::Denied,
370 Err(e) => {
371 warn!(
372 "Failed to receive permission state from embedder ({:?}).",
373 e
374 );
375 PermissionState::Denied
376 },
377 }
378}
379
380impl Convert<PermissionFeature> for PermissionName {
381 fn convert(self) -> PermissionFeature {
382 match self {
383 PermissionName::Geolocation => PermissionFeature::Geolocation,
384 PermissionName::Notifications => PermissionFeature::Notifications,
385 PermissionName::Push => PermissionFeature::Push,
386 PermissionName::Midi => PermissionFeature::Midi,
387 PermissionName::Camera => PermissionFeature::Camera,
388 PermissionName::Microphone => PermissionFeature::Microphone,
389 PermissionName::Speaker => PermissionFeature::Speaker,
390 PermissionName::Device_info => PermissionFeature::DeviceInfo,
391 PermissionName::Background_sync => PermissionFeature::BackgroundSync,
392 PermissionName::Bluetooth => PermissionFeature::Bluetooth,
393 PermissionName::Persistent_storage => PermissionFeature::PersistentStorage,
394 PermissionName::Screen_wake_lock => {
395 PermissionFeature::ScreenWakeLock(WakeLockType::Screen)
396 },
397 PermissionName::Gamepad => PermissionFeature::Gamepad,
398 }
399 }
400}