diff options
Diffstat (limited to 'components/script/dom/permissions.rs')
-rw-r--r-- | components/script/dom/permissions.rs | 320 |
1 files changed, 176 insertions, 144 deletions
diff --git a/components/script/dom/permissions.rs b/components/script/dom/permissions.rs index b5cab2ace90..b4e6bf8f5de 100644 --- a/components/script/dom/permissions.rs +++ b/components/script/dom/permissions.rs @@ -1,46 +1,51 @@ /* 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/. */ - -use dom::bindings::codegen::Bindings::PermissionStatusBinding::{PermissionDescriptor, PermissionName, PermissionState}; -use dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionStatusMethods; -use dom::bindings::codegen::Bindings::PermissionsBinding::{self, PermissionsMethods}; -use dom::bindings::error::Error; -use dom::bindings::js::Root; -use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; -use dom::bluetooth::Bluetooth; -use dom::bluetoothpermissionresult::BluetoothPermissionResult; -use dom::globalscope::GlobalScope; -use dom::permissionstatus::PermissionStatus; -use dom::promise::Promise; + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionDescriptor; +use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionStatusMethods; +use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::{ + PermissionName, PermissionState, +}; +use crate::dom::bindings::codegen::Bindings::PermissionsBinding::PermissionsMethods; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bluetooth::Bluetooth; +use crate::dom::bluetoothpermissionresult::BluetoothPermissionResult; +use crate::dom::globalscope::GlobalScope; +use crate::dom::permissionstatus::PermissionStatus; +use crate::dom::promise::Promise; +use crate::realms::{AlreadyInRealm, InRealm}; +use crate::script_runtime::JSContext; use dom_struct::dom_struct; +use embedder_traits::{self, EmbedderMsg, PermissionPrompt, PermissionRequest}; +use ipc_channel::ipc; use js::conversions::ConversionResult; -use js::jsapi::{JSContext, JSObject}; +use js::jsapi::JSObject; use js::jsval::{ObjectValue, UndefinedValue}; -#[cfg(target_os = "linux")] -use servo_config::opts; -use servo_config::prefs::PREFS; +use servo_config::pref; use std::rc::Rc; -#[cfg(target_os = "linux")] -use tinyfiledialogs::{self, MessageBoxIcon, YesNo}; - -#[cfg(target_os = "linux")] -const DIALOG_TITLE: &'static str = "Permission request dialog"; -const NONSECURE_DIALOG_MESSAGE: &'static str = "feature is only safe to use in secure context,\ - but servo can't guarantee\n that the current context is secure. Do you want to proceed and grant permission?"; -const REQUEST_DIALOG_MESSAGE: &'static str = "Do you want to grant permission for"; -const ROOT_DESC_CONVERSION_ERROR: &'static str = "Can't convert to an IDL value of type PermissionDescriptor"; pub trait PermissionAlgorithm { type Descriptor; type Status; - fn create_descriptor(cx: *mut JSContext, - permission_descriptor_obj: *mut JSObject) - -> Result<Self::Descriptor, Error>; - fn permission_query(cx: *mut JSContext, promise: &Rc<Promise>, - descriptor: &Self::Descriptor, status: &Self::Status); - fn permission_request(cx: *mut JSContext, promise: &Rc<Promise>, - descriptor: &Self::Descriptor, status: &Self::Status); + fn create_descriptor( + cx: JSContext, + permission_descriptor_obj: *mut JSObject, + ) -> Result<Self::Descriptor, Error>; + fn permission_query( + cx: JSContext, + promise: &Rc<Promise>, + descriptor: &Self::Descriptor, + status: &Self::Status, + ); + fn permission_request( + cx: JSContext, + promise: &Rc<Promise>, + descriptor: &Self::Descriptor, + status: &Self::Status, + ); fn permission_revoke(descriptor: &Self::Descriptor, status: &Self::Status); } @@ -63,33 +68,35 @@ impl Permissions { } } - pub fn new(global: &GlobalScope) -> Root<Permissions> { - reflect_dom_object(box Permissions::new_inherited(), - global, - PermissionsBinding::Wrap) + pub fn new(global: &GlobalScope) -> DomRoot<Permissions> { + reflect_dom_object(Box::new(Permissions::new_inherited()), global) } - #[allow(unrooted_must_root)] // https://w3c.github.io/permissions/#dom-permissions-query // https://w3c.github.io/permissions/#dom-permissions-request // https://w3c.github.io/permissions/#dom-permissions-revoke - fn manipulate(&self, - op: Operation, - cx: *mut JSContext, - permissionDesc: *mut JSObject, - promise: Option<Rc<Promise>>) - -> Rc<Promise> { + #[allow(non_snake_case)] + fn manipulate( + &self, + op: Operation, + cx: JSContext, + permissionDesc: *mut JSObject, + promise: Option<Rc<Promise>>, + ) -> Rc<Promise> { // (Query, Request) Step 3. let p = match promise { Some(promise) => promise, - None => Promise::new(&self.global()), + None => { + let in_realm_proof = AlreadyInRealm::assert(&self.global()); + Promise::new_in_current_realm(&self.global(), InRealm::Already(&in_realm_proof)) + }, }; // (Query, Request, Revoke) Step 1. let root_desc = match Permissions::create_descriptor(cx, permissionDesc) { Ok(descriptor) => descriptor, Err(error) => { - p.reject_error(cx, error); + p.reject_error(error); return p; }, }; @@ -103,7 +110,7 @@ impl Permissions { let bluetooth_desc = match Bluetooth::create_descriptor(cx, permissionDesc) { Ok(descriptor) => descriptor, Err(error) => { - p.reject_error(cx, error); + p.reject_error(error); return p; }, }; @@ -113,18 +120,22 @@ impl Permissions { match &op { // (Request) Step 6 - 8. - &Operation::Request => Bluetooth::permission_request(cx, &p, &bluetooth_desc, &result), + &Operation::Request => { + Bluetooth::permission_request(cx, &p, &bluetooth_desc, &result) + }, // (Query) Step 6 - 7. - &Operation::Query => Bluetooth::permission_query(cx, &p, &bluetooth_desc, &result), + &Operation::Query => { + Bluetooth::permission_query(cx, &p, &bluetooth_desc, &result) + }, &Operation::Revoke => { // (Revoke) Step 3. let globalscope = self.global(); - globalscope.as_window() - .permission_state_invocation_results() - .borrow_mut() - .remove(&root_desc.name.to_string()); + globalscope + .permission_state_invocation_results() + .borrow_mut() + .remove(&root_desc.name.to_string()); // (Revoke) Step 4. Bluetooth::permission_revoke(&bluetooth_desc, &result) @@ -140,23 +151,23 @@ impl Permissions { // (Request) Step 7. The default algorithm always resolve // (Request) Step 8. - p.resolve_native(cx, &status); + p.resolve_native(&status); }, &Operation::Query => { // (Query) Step 6. Permissions::permission_query(cx, &p, &root_desc, &status); // (Query) Step 7. - p.resolve_native(cx, &status); + p.resolve_native(&status); }, &Operation::Revoke => { // (Revoke) Step 3. let globalscope = self.global(); - globalscope.as_window() - .permission_state_invocation_results() - .borrow_mut() - .remove(&root_desc.name.to_string()); + globalscope + .permission_state_invocation_results() + .borrow_mut() + .remove(&root_desc.name.to_string()); // (Revoke) Step 4. Permissions::permission_revoke(&root_desc, &status); @@ -174,25 +185,20 @@ impl Permissions { } } +#[allow(non_snake_case)] impl PermissionsMethods for Permissions { - #[allow(unrooted_must_root)] - #[allow(unsafe_code)] // https://w3c.github.io/permissions/#dom-permissions-query - unsafe fn Query(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> { + fn Query(&self, cx: JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> { self.manipulate(Operation::Query, cx, permissionDesc, None) } - #[allow(unrooted_must_root)] - #[allow(unsafe_code)] // https://w3c.github.io/permissions/#dom-permissions-request - unsafe fn Request(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> { + fn Request(&self, cx: JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> { self.manipulate(Operation::Request, cx, permissionDesc, None) } - #[allow(unrooted_must_root)] - #[allow(unsafe_code)] // https://w3c.github.io/permissions/#dom-permissions-revoke - unsafe fn Revoke(&self, cx: *mut JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> { + fn Revoke(&self, cx: JSContext, permissionDesc: *mut JSObject) -> Rc<Promise> { self.manipulate(Operation::Revoke, cx, permissionDesc, None) } } @@ -201,35 +207,39 @@ impl PermissionAlgorithm for Permissions { type Descriptor = PermissionDescriptor; type Status = PermissionStatus; - #[allow(unsafe_code)] - fn create_descriptor(cx: *mut JSContext, - permission_descriptor_obj: *mut JSObject) - -> Result<PermissionDescriptor, Error> { - rooted!(in(cx) let mut property = UndefinedValue()); - property.handle_mut().set(ObjectValue(permission_descriptor_obj)); - unsafe { - match PermissionDescriptor::new(cx, property.handle()) { - Ok(ConversionResult::Success(descriptor)) => Ok(descriptor), - Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into_owned())), - Err(_) => Err(Error::Type(String::from(ROOT_DESC_CONVERSION_ERROR))), - } + fn create_descriptor( + cx: JSContext, + permission_descriptor_obj: *mut JSObject, + ) -> Result<PermissionDescriptor, Error> { + rooted!(in(*cx) let mut property = UndefinedValue()); + property + .handle_mut() + .set(ObjectValue(permission_descriptor_obj)); + match PermissionDescriptor::new(cx, property.handle()) { + Ok(ConversionResult::Success(descriptor)) => Ok(descriptor), + Ok(ConversionResult::Failure(error)) => Err(Error::Type(error.into_owned())), + Err(_) => Err(Error::JSFailed), } } // https://w3c.github.io/permissions/#boolean-permission-query-algorithm - fn permission_query(_cx: *mut JSContext, - _promise: &Rc<Promise>, - _descriptor: &PermissionDescriptor, - status: &PermissionStatus) { + fn permission_query( + _cx: JSContext, + _promise: &Rc<Promise>, + _descriptor: &PermissionDescriptor, + status: &PermissionStatus, + ) { // Step 1. status.set_state(get_descriptor_permission_state(status.get_query(), None)); } // https://w3c.github.io/permissions/#boolean-permission-request-algorithm - fn permission_request(cx: *mut JSContext, - promise: &Rc<Promise>, - descriptor: &PermissionDescriptor, - status: &PermissionStatus) { + fn permission_request( + cx: JSContext, + promise: &Rc<Promise>, + descriptor: &PermissionDescriptor, + status: &PermissionStatus, + ) { // Step 1. Permissions::permission_query(cx, promise, descriptor, status); @@ -237,15 +247,16 @@ impl PermissionAlgorithm for Permissions { // Step 3. PermissionState::Prompt => { let perm_name = status.get_query(); + let prompt = + PermissionPrompt::Request(embedder_traits::PermissionName::from(perm_name)); + // https://w3c.github.io/permissions/#request-permission-to-use (Step 3 - 4) - let state = - prompt_user(&format!("{} {} ?", REQUEST_DIALOG_MESSAGE, perm_name.clone())); - - let globalscope = GlobalScope::current(); - globalscope.as_window() - .permission_state_invocation_results() - .borrow_mut() - .insert(perm_name.to_string(), state); + let globalscope = GlobalScope::current().expect("No current global object"); + let state = prompt_user_from_embedder(prompt, &globalscope); + globalscope + .permission_state_invocation_results() + .borrow_mut() + .insert(perm_name.to_string(), state); }, // Step 2. @@ -260,13 +271,14 @@ impl PermissionAlgorithm for Permissions { } // https://w3c.github.io/permissions/#permission-state -pub fn get_descriptor_permission_state(permission_name: PermissionName, - env_settings_obj: Option<&GlobalScope>) - -> PermissionState { +pub fn get_descriptor_permission_state( + permission_name: PermissionName, + env_settings_obj: Option<&GlobalScope>, +) -> PermissionState { // Step 1. - let settings = match env_settings_obj { - Some(env_settings_obj) => Root::from_ref(env_settings_obj), - None => GlobalScope::current(), + let globalscope = match env_settings_obj { + Some(env_settings_obj) => DomRoot::from_ref(env_settings_obj), + None => GlobalScope::current().expect("No current global object"), }; // Step 2. @@ -274,60 +286,43 @@ pub fn get_descriptor_permission_state(permission_name: PermissionName, // The current solution is a workaround with a message box to warn about this, // if the feature is not allowed in non-secure contexcts, // and let the user decide to grant the permission or not. - let state = match allowed_in_nonsecure_contexts(&permission_name) { - true => PermissionState::Prompt, - false => { - match PREFS.get("dom.permissions.testing.allowed_in_nonsecure_contexts").as_boolean().unwrap_or(false) { - true => PermissionState::Granted, - false => { - settings.as_window() - .permission_state_invocation_results() - .borrow_mut() - .remove(&permission_name.to_string()); - prompt_user(&format!("The {} {}", permission_name, NONSECURE_DIALOG_MESSAGE)) - }, - } - }, + let state = if allowed_in_nonsecure_contexts(&permission_name) { + PermissionState::Prompt + } else { + if pref!(dom.permissions.testing.allowed_in_nonsecure_contexts) { + PermissionState::Granted + } else { + globalscope + .permission_state_invocation_results() + .borrow_mut() + .remove(&permission_name.to_string()); + + prompt_user_from_embedder( + PermissionPrompt::Insecure(embedder_traits::PermissionName::from(permission_name)), + &globalscope, + ) + } }; // Step 3. - if let Some(prev_result) = settings.as_window() - .permission_state_invocation_results() - .borrow() - .get(&permission_name.to_string()) { + if let Some(prev_result) = globalscope + .permission_state_invocation_results() + .borrow() + .get(&permission_name.to_string()) + { return prev_result.clone(); } // Store the invocation result - settings.as_window() - .permission_state_invocation_results() - .borrow_mut() - .insert(permission_name.to_string(), state); + globalscope + .permission_state_invocation_results() + .borrow_mut() + .insert(permission_name.to_string(), state); // Step 4. state } -#[cfg(target_os = "linux")] -fn prompt_user(message: &str) -> PermissionState { - if opts::get().headless { - return PermissionState::Denied; - } - match tinyfiledialogs::message_box_yes_no(DIALOG_TITLE, - message, - MessageBoxIcon::Question, - YesNo::No) { - YesNo::Yes => PermissionState::Granted, - YesNo::No => PermissionState::Denied, - } -} - -#[cfg(not(target_os = "linux"))] -fn prompt_user(_message: &str) -> PermissionState { - // TODO popup only supported on linux - PermissionState::Denied -} - // https://w3c.github.io/permissions/#allowed-in-non-secure-contexts fn allowed_in_nonsecure_contexts(permission_name: &PermissionName) -> bool { match *permission_name { @@ -355,3 +350,40 @@ fn allowed_in_nonsecure_contexts(permission_name: &PermissionName) -> bool { PermissionName::Persistent_storage => false, } } + +fn prompt_user_from_embedder(prompt: PermissionPrompt, gs: &GlobalScope) -> PermissionState { + let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!"); + gs.send_to_embedder(EmbedderMsg::PromptPermission(prompt, sender)); + + match receiver.recv() { + Ok(PermissionRequest::Granted) => PermissionState::Granted, + Ok(PermissionRequest::Denied) => PermissionState::Denied, + Err(e) => { + warn!( + "Failed to receive permission state from embedder ({:?}).", + e + ); + PermissionState::Denied + }, + } +} + +impl From<PermissionName> for embedder_traits::PermissionName { + fn from(permission_name: PermissionName) -> Self { + match permission_name { + PermissionName::Geolocation => embedder_traits::PermissionName::Geolocation, + PermissionName::Notifications => embedder_traits::PermissionName::Notifications, + PermissionName::Push => embedder_traits::PermissionName::Push, + PermissionName::Midi => embedder_traits::PermissionName::Midi, + PermissionName::Camera => embedder_traits::PermissionName::Camera, + PermissionName::Microphone => embedder_traits::PermissionName::Microphone, + PermissionName::Speaker => embedder_traits::PermissionName::Speaker, + PermissionName::Device_info => embedder_traits::PermissionName::DeviceInfo, + PermissionName::Background_sync => embedder_traits::PermissionName::BackgroundSync, + PermissionName::Bluetooth => embedder_traits::PermissionName::Bluetooth, + PermissionName::Persistent_storage => { + embedder_traits::PermissionName::PersistentStorage + }, + } + } +} |