aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/script/dom/mod.rs2
-rw-r--r--components/script/dom/navigator.rs10
-rw-r--r--components/script/dom/permissions.rs262
-rw-r--r--components/script/dom/permissionstatus.rs55
-rw-r--r--components/script/dom/webidls/Navigator.webidl6
-rw-r--r--components/script/dom/webidls/PermissionStatus.webidl47
-rw-r--r--components/script/dom/webidls/Permissions.webidl14
-rw-r--r--components/script/dom/webidls/WorkerNavigator.webidl7
-rw-r--r--components/script/dom/workernavigator.rs12
-rw-r--r--tests/html/permission-test.html37
10 files changed, 449 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()))
+ }
}
diff --git a/tests/html/permission-test.html b/tests/html/permission-test.html
new file mode 100644
index 00000000000..fa1c9183f98
--- /dev/null
+++ b/tests/html/permission-test.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<title>Permission Test</title>
+<body>
+ <button type="button" onclick="onQueryButtonClick()">query</button>
+ <button type="button" onclick="onRequestButtonClick()">request</button>
+ <button type="button" onclick="onRevokeButtonClick()">revoke</button>
+ <input type="text" id="permissionName" value="geolocation"></input>
+ <pre id="log"></pre>
+ <script>
+ function onQueryButtonClick() {
+ let permissionName = document.getElementById('permissionName').value;
+ let permissionDescriptor = {name: permissionName};
+ window.navigator.permissions.query(permissionDescriptor)
+ .then(status => log("permission status of " + permissionName + " is: " + status.state))
+ .catch(err => log(err));
+ }
+ function onRequestButtonClick() {
+ let permissionName = document.getElementById('permissionName').value;
+ let permissionDescriptor = {name: permissionName};
+ window.navigator.permissions.request(permissionDescriptor)
+ .then(status => log("permission status of " + permissionName + " is: " + status.state))
+ .catch(err => log(err));
+ }
+ function onRevokeButtonClick() {
+ let permissionName = document.getElementById('permissionName').value;
+ let permissionDescriptor = {name: permissionName};
+ window.navigator.permissions.revoke(permissionDescriptor)
+ .then(status => log("permission status of " + permissionName + " is: " + status.state))
+ .catch(err => log(err));
+ }
+ function log(line) {
+ document.getElementById("log").textContent += line + '\n';
+ }
+ </script>
+</body>
+</html>