diff options
author | Attila Dusnoki <dati91@gmail.com> | 2017-01-30 11:29:04 +0100 |
---|---|---|
committer | Attila Dusnoki <dati91@gmail.com> | 2017-02-13 14:35:52 +0100 |
commit | 5ca3ee947431afe7af3103db8353a61510155583 (patch) | |
tree | a16a1ed0b4b64be3da411f6493c1f5ad90cbbcc3 /components/script/dom | |
parent | e394334739635e58bc4d160e9d27bf7217945746 (diff) | |
download | servo-5ca3ee947431afe7af3103db8353a61510155583.tar.gz servo-5ca3ee947431afe7af3103db8353a61510155583.zip |
Permission API
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/mod.rs | 2 | ||||
-rw-r--r-- | components/script/dom/navigator.rs | 10 | ||||
-rw-r--r-- | components/script/dom/permissions.rs | 262 | ||||
-rw-r--r-- | components/script/dom/permissionstatus.rs | 55 | ||||
-rw-r--r-- | components/script/dom/webidls/Navigator.webidl | 6 | ||||
-rw-r--r-- | components/script/dom/webidls/PermissionStatus.webidl | 47 | ||||
-rw-r--r-- | components/script/dom/webidls/Permissions.webidl | 14 | ||||
-rw-r--r-- | components/script/dom/webidls/WorkerNavigator.webidl | 7 | ||||
-rw-r--r-- | components/script/dom/workernavigator.rs | 12 |
9 files changed, 412 insertions, 3 deletions
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 5de594a5195..b1541a225b7 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -384,6 +384,8 @@ pub mod nodelist; pub mod pagetransitionevent; pub mod performance; pub mod performancetiming; +pub mod permissions; +pub mod permissionstatus; pub mod plugin; pub mod pluginarray; pub mod popstateevent; diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index dd9a1a789c6..27a1465e9d2 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -10,6 +10,7 @@ use dom::bindings::str::DOMString; use dom::bluetooth::Bluetooth; use dom::mimetypearray::MimeTypeArray; use dom::navigatorinfo; +use dom::permissions::Permissions; use dom::pluginarray::PluginArray; use dom::serviceworkercontainer::ServiceWorkerContainer; use dom::vr::VR; @@ -23,7 +24,8 @@ pub struct Navigator { plugins: MutNullableJS<PluginArray>, mime_types: MutNullableJS<MimeTypeArray>, service_worker: MutNullableJS<ServiceWorkerContainer>, - vr: MutNullableJS<VR> + vr: MutNullableJS<VR>, + permissions: MutNullableJS<Permissions>, } impl Navigator { @@ -35,6 +37,7 @@ impl Navigator { mime_types: Default::default(), service_worker: Default::default(), vr: Default::default(), + permissions: Default::default(), } } @@ -123,6 +126,11 @@ impl NavigatorMethods for Navigator { fn Vr(&self) -> Root<VR> { self.vr.or_init(|| VR::new(&self.global())) } + + // https://w3c.github.io/permissions/#navigator-and-workernavigator-extension + fn Permissions(&self) -> Root<Permissions> { + self.permissions.or_init(|| Permissions::new(&self.global())) + } } impl Navigator { diff --git a/components/script/dom/permissions.rs b/components/script/dom/permissions.rs new file mode 100644 index 00000000000..ebece7374a6 --- /dev/null +++ b/components/script/dom/permissions.rs @@ -0,0 +1,262 @@ +/* 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::PermissionsBinding::{self, PermissionsMethods}; +use dom::bindings::error::Error; +use dom::bindings::js::Root; +use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; +use dom::globalscope::GlobalScope; +use dom::permissionstatus::PermissionStatus; +use dom::promise::Promise; +use js::conversions::ConversionResult; +use js::jsapi::{JSContext, JSObject}; +use js::jsval::{ObjectValue, UndefinedValue}; +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"; +#[cfg(target_os = "linux")] +const QUERY_DIALOG_MESSAGE: &'static str = "Can't guarantee, that the current context is secure. +\t\tStill 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 permission_revoke(descriptor: &Self::Descriptor, status: &Self::Status); +} + +// https://w3c.github.io/permissions/#permissions +#[dom_struct] +pub struct Permissions { + reflector_: Reflector, +} + +impl Permissions { + pub fn new_inherited() -> Permissions { + Permissions { + reflector_: Reflector::new(), + } + } + + pub fn new(global: &GlobalScope) -> Root<Permissions> { + reflect_dom_object(box Permissions::new_inherited(), + global, + PermissionsBinding::Wrap) + } +} + +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> { + // Step 3. + let p = Promise::new(&self.global()); + + // Step 1. + let root_desc = match Permissions::create_descriptor(cx, permissionDesc) { + Ok(descriptor) => descriptor, + Err(error) => { + p.reject_error(cx, error); + return p; + }, + }; + + // Step 5. + let status = PermissionStatus::new(&self.global(), &root_desc); + + // Step 2. + match root_desc.name { + _ => { + // Step 6. + Permissions::permission_query(cx, &p, &root_desc, &status); + + // Step 7. + p.resolve_native(cx, &status); + }, + }; + + // Step 4. + return p; + } + + #[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> { + // Step 3. + let p = Promise::new(&self.global()); + + // Step 1. + let root_desc = match Permissions::create_descriptor(cx, permissionDesc) { + Ok(descriptor) => descriptor, + Err(error) => { + p.reject_error(cx, error); + return p; + }, + }; + + // Step 5. + let status = PermissionStatus::new(&self.global(), &root_desc); + + // Step 2. + match root_desc.name { + _ => { + // Step 6. + Permissions::permission_request(cx, &p, &root_desc, &status); + + // Step 7. The default algorithm always resolve + + // Step 8. + p.resolve_native(cx, &status); + }, + }; + // Step 4. + return p; + } + + #[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> { + // Step 1. + let root_desc = match Permissions::create_descriptor(cx, permissionDesc) { + Ok(descriptor) => descriptor, + Err(error) => { + let p = Promise::new(&self.global()); + p.reject_error(cx, error); + return p; + }, + }; + + let status = PermissionStatus::new(&self.global(), &root_desc); + + // Step 2. + match root_desc.name { + _ => { + Permissions::permission_revoke(&root_desc, &status); + }, + }; + + // Step 5. + return self.Query(cx, permissionDesc); + } +} + +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))), + } + } + } + + // https://w3c.github.io/permissions/#boolean-permission-query-algorithm + fn permission_query(_cx: *mut 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) { + // Step 1. + Permissions::permission_query(cx, promise, descriptor, status); + + // TODO: Step 2 - 4: `environment settings object` is not implemented in Servo yet. + // For this reason in the `get_descriptor_permission_state` function we can't decide + // if we have a secure context or not, or store the previous invocation results. + // Without these the remaining steps can't be implemented properly. + } + + fn permission_revoke(_descriptor: &PermissionDescriptor, _status: &PermissionStatus) {} +} + +// https://w3c.github.io/permissions/#permission-state +pub fn get_descriptor_permission_state(permission_name: PermissionName , + _env_settings_obj: Option<*mut JSObject>) + -> PermissionState { + // TODO: Step 1: If settings wasn’t passed, set it to the current settings object. + // TODO: `environment settings object` is not implemented in Servo yet. + + // Step 2. + // TODO: The `is the environment settings object a non-secure context` check is missing. + // 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. + if !allowed_in_nonsecure_contexts(&permission_name) { + if cfg!(target_os = "linux") { + match tinyfiledialogs::message_box_yes_no(DIALOG_TITLE, + &format!("{} {:?} ?", QUERY_DIALOG_MESSAGE, permission_name), + MessageBoxIcon::Question, + YesNo::No) { + YesNo::Yes => return PermissionState::Granted, + YesNo::No => return PermissionState::Denied, + }; + } else { + return PermissionState::Denied; + } + } + + // TODO: Step 3: Store the invocation results + // TODO: `environment settings object` is not implemented in Servo yet. + + // Step 4. + PermissionState::Granted +} + +// https://w3c.github.io/permissions/#allowed-in-non-secure-contexts +fn allowed_in_nonsecure_contexts(permission_name: &PermissionName) -> bool { + match *permission_name { + // https://w3c.github.io/permissions/#dom-permissionname-geolocation + PermissionName::Geolocation => true, + // https://w3c.github.io/permissions/#dom-permissionname-notifications + PermissionName::Notifications => true, + // https://w3c.github.io/permissions/#dom-permissionname-push + PermissionName::Push => false, + // https://w3c.github.io/permissions/#dom-permissionname-midi + PermissionName::Midi => true, + // https://w3c.github.io/permissions/#dom-permissionname-camera + PermissionName::Camera => false, + // https://w3c.github.io/permissions/#dom-permissionname-microphone + PermissionName::Microphone => false, + // https://w3c.github.io/permissions/#dom-permissionname-speaker + PermissionName::Speaker => false, + // https://w3c.github.io/permissions/#dom-permissionname-device-info + PermissionName::Device_info => false, + // https://w3c.github.io/permissions/#dom-permissionname-background-sync + PermissionName::Background_sync => false, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-permissionname-bluetooth + PermissionName::Bluetooth => false, + // https://storage.spec.whatwg.org/#dom-permissionname-persistent-storage + PermissionName::Persistent_storage => false, + } +} diff --git a/components/script/dom/permissionstatus.rs b/components/script/dom/permissionstatus.rs new file mode 100644 index 00000000000..51615c4c3cc --- /dev/null +++ b/components/script/dom/permissionstatus.rs @@ -0,0 +1,55 @@ +/* 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 core::clone::Clone; +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; +use dom::bindings::codegen::Bindings::PermissionStatusBinding::{self, PermissionDescriptor, PermissionName}; +use dom::bindings::codegen::Bindings::PermissionStatusBinding::{PermissionState, PermissionStatusMethods}; +use dom::bindings::js::Root; +use dom::bindings::reflector::reflect_dom_object; +use dom::eventtarget::EventTarget; +use dom::globalscope::GlobalScope; + +// https://w3c.github.io/permissions/#permissionstatus +#[dom_struct] +pub struct PermissionStatus { + eventtarget: EventTarget, + state: DOMRefCell<PermissionState>, + query: DOMRefCell<PermissionName>, +} + +impl PermissionStatus { + pub fn new_inherited(query: PermissionName) -> PermissionStatus { + PermissionStatus { + eventtarget: EventTarget::new_inherited(), + state: DOMRefCell::new(PermissionState::Denied), + query: DOMRefCell::new(query), + } + } + + pub fn new(global: &GlobalScope, query: &PermissionDescriptor) -> Root<PermissionStatus> { + reflect_dom_object(box PermissionStatus::new_inherited(query.name), + global, + PermissionStatusBinding::Wrap) + } + + pub fn set_state(&self, state: PermissionState) { + *self.state.borrow_mut() = state; + } + + pub fn get_query(&self) -> PermissionName { + self.query.borrow().clone() + } +} + +impl PermissionStatusMethods for PermissionStatus { + // https://w3c.github.io/permissions/#dom-permissionstatus-state + fn State(&self) -> PermissionState { + self.state.borrow().clone() + } + + // https://w3c.github.io/permissions/#dom-permissionstatus-onchange + event_handler!(onchange, GetOnchange, SetOnchange); +} diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl index a60a1541446..a461b01932b 100644 --- a/components/script/dom/webidls/Navigator.webidl +++ b/components/script/dom/webidls/Navigator.webidl @@ -62,3 +62,9 @@ interface NavigatorCookies { partial interface Navigator { [SameObject, Pref="dom.webvr.enabled"] readonly attribute VR vr; }; + +// https://w3c.github.io/permissions/#navigator-and-workernavigator-extension +[Exposed=(Window)] +partial interface Navigator { + readonly attribute Permissions permissions; +}; diff --git a/components/script/dom/webidls/PermissionStatus.webidl b/components/script/dom/webidls/PermissionStatus.webidl new file mode 100644 index 00000000000..168d797685e --- /dev/null +++ b/components/script/dom/webidls/PermissionStatus.webidl @@ -0,0 +1,47 @@ +/* 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/. */ + + // https://w3c.github.io/permissions/#permissionstatus + +dictionary PermissionDescriptor { + required PermissionName name; +}; + +enum PermissionState { + "granted", + "denied", + "prompt", +}; + +enum PermissionName { + "geolocation", + "notifications", + "push", + "midi", + "camera", + "microphone", + "speaker", + "device-info", + "background-sync", + "bluetooth", + "persistent-storage", +}; + +[Exposed=(Window,Worker)] +interface PermissionStatus : EventTarget { + readonly attribute PermissionState state; + attribute EventHandler onchange; +}; + +dictionary PushPermissionDescriptor : PermissionDescriptor { + boolean userVisibleOnly = false; +}; + +dictionary MidiPermissionDescriptor : PermissionDescriptor { + boolean sysex = false; +}; + +dictionary DevicePermissionDescriptor : PermissionDescriptor { + DOMString deviceId; +}; diff --git a/components/script/dom/webidls/Permissions.webidl b/components/script/dom/webidls/Permissions.webidl new file mode 100644 index 00000000000..279acb09ba3 --- /dev/null +++ b/components/script/dom/webidls/Permissions.webidl @@ -0,0 +1,14 @@ +/* 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/. */ + +// https://w3c.github.io/permissions/#permissions-interface + +[Exposed=(Window,Worker)] +interface Permissions { + Promise<PermissionStatus> query(object permissionDesc); + + Promise<PermissionStatus> request(object permissionDesc); + + Promise<PermissionStatus> revoke(object permissionDesc); +}; diff --git a/components/script/dom/webidls/WorkerNavigator.webidl b/components/script/dom/webidls/WorkerNavigator.webidl index 0661325b8be..10c258f167b 100644 --- a/components/script/dom/webidls/WorkerNavigator.webidl +++ b/components/script/dom/webidls/WorkerNavigator.webidl @@ -8,3 +8,10 @@ interface WorkerNavigator {}; WorkerNavigator implements NavigatorID; WorkerNavigator implements NavigatorLanguage; //WorkerNavigator implements NavigatorOnLine; + +// https://w3c.github.io/permissions/#navigator-and-workernavigator-extension + +[Exposed=(Worker)] +partial interface WorkerNavigator { + readonly attribute Permissions permissions; +}; diff --git a/components/script/dom/workernavigator.rs b/components/script/dom/workernavigator.rs index f6cd521634d..d2a5a7da7d5 100644 --- a/components/script/dom/workernavigator.rs +++ b/components/script/dom/workernavigator.rs @@ -4,22 +4,25 @@ use dom::bindings::codegen::Bindings::WorkerNavigatorBinding; use dom::bindings::codegen::Bindings::WorkerNavigatorBinding::WorkerNavigatorMethods; -use dom::bindings::js::Root; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::js::{MutNullableJS, Root}; +use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::navigatorinfo; +use dom::permissions::Permissions; use dom::workerglobalscope::WorkerGlobalScope; // https://html.spec.whatwg.org/multipage/#workernavigator #[dom_struct] pub struct WorkerNavigator { reflector_: Reflector, + permissions: MutNullableJS<Permissions>, } impl WorkerNavigator { fn new_inherited() -> WorkerNavigator { WorkerNavigator { reflector_: Reflector::new(), + permissions: Default::default(), } } @@ -70,4 +73,9 @@ impl WorkerNavigatorMethods for WorkerNavigator { fn Language(&self) -> DOMString { navigatorinfo::Language() } + + // https://w3c.github.io/permissions/#navigator-and-workernavigator-extension + fn Permissions(&self) -> Root<Permissions> { + self.permissions.or_init(|| Permissions::new(&self.global())) + } } |